#include <string.h>
#include <math.h>

#include "global.h"

extern short iKingOfTheHillZoneLimits[4][4];
extern void PlayerKilledPlayer(short iKiller, CPlayer * killed, short deathstyle, short killstyle, bool fForce, bool fKillCarriedItem);
extern void PlayerKilledPlayer(CPlayer * killer, CPlayer * killed, short deathstyle, short killstyle, bool fForce, bool fKillCarriedItem);
extern void AddAwardKill(CPlayer * killer, CPlayer * killed, killstyle style);
extern bool SwapPlayers(short iUsingPlayerID);
extern short scorepowerupoffsets[3][3];

extern CPlayer * GetPlayerFromGlobalID(short iGlobalID);
extern void CheckSecret(short id);
extern SpotlightManager spotlightManager;

void removeifprojectile(IO_MovingObject * object, bool playsound, bool forcedead)
{
	if(object->dead)
		return;

	MovingObjectType type = object->movingObjectType;
	if(type == movingobject_fireball || type == movingobject_hammer || type == movingobject_boomerang || type == movingobject_iceblast || type == movingobject_superfireball || type == movingobject_sledgehammer)
	{
		short iPlayerID = object->iPlayerID;
		bool fDie = true;

		if(type == movingobject_hammer && !game_values.hammerpower)
		{
			fDie = false;
		}

		if(fDie || forcedead)
		{
			if(iPlayerID > -1 && projectiles[iPlayerID] > 0)
				projectiles[iPlayerID]--;

			object->dead = true;
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, object->ix + (object->iw >> 1) - 16, object->iy + (object->ih >> 1) - 16, 3, 4));
		}
		
		if(playsound)
			ifsoundonplay(sfx_hit);
	}
}

IO_MovingObject * createpowerup(short iType, short ix, short iy, bool side, bool spawn)
{
	MO_Powerup * powerup = NULL;
	CO_Shell * shell = NULL;
	PU_FeatherPowerup * feather = NULL;
	MO_Coin * coin = NULL;

	short iSpawnX = ix + 1;
	short iSpawnY = iy - 1;

	switch(iType)
	{
		case HEALTH_POWERUP:
		{
			powerup = new PU_ExtraHeartPowerup(&spr_extraheartpowerup, iSpawnX, iSpawnY);
			break;
		}
		case TIME_POWERUP:
		{
			powerup = new PU_ExtraTimePowerup(&spr_extratimepowerup, iSpawnX, iSpawnY);
			break;
		}
		case JAIL_KEY_POWERUP:
		{
			powerup = new PU_JailKeyPowerup(&spr_jailkeypowerup, iSpawnX, iSpawnY);
			break;
		}
		case COIN_POWERUP:
		{
			short iRandCoin = rand() % 9;
			short iCoin = 2;

			if(iRandCoin == 8)
				iCoin = 3;
			else if(iRandCoin >= 6)
				iCoin = 1;
			else if(iRandCoin >= 3)
				iCoin = 0;

			static short iCoinValue[4] = {3, 5, 2, 10};
			static short iGreedValue[4] = {10, 15, 5, 20};

			powerup = new PU_CoinPowerup(&spr_coin, iSpawnX, iSpawnY, iCoin, game_values.gamemode->gamemode == game_mode_greed ? iGreedValue[iCoin] : iCoinValue[iCoin]);
			break;
		}
		case MINIGAME_COIN:
		{
			coin = new MO_Coin(&spr_coin, 0.0f, -VELJUMP / 2.0, iSpawnX, iSpawnY, 2, -1, 2, 0, false);
			break;
		}
		case SECRET1_POWERUP:
		{
			powerup = new PU_SecretPowerup(&spr_secret1, iSpawnX, iSpawnY, 0);
			break;
		}
		case SECRET2_POWERUP:
		{
			powerup = new PU_SecretPowerup(&spr_secret2, iSpawnX, iSpawnY, 1);
			break;
		}
		case SECRET3_POWERUP:
		{
			powerup = new PU_SecretPowerup(&spr_secret3, iSpawnX, iSpawnY, 2);
			break;
		}
		case SECRET4_POWERUP:
		{
			powerup = new PU_SecretPowerup(&spr_secret4, iSpawnX, iSpawnY, 3);
			break;
		}
		case 0:
		{
			powerup = new PU_PoisonPowerup(&spr_poisonpowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1);
			break;
		}
		case 1:
		{
			powerup = new PU_ExtraGuyPowerup(&spr_1uppowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1, 1);
			break;
		}
		case 2:
		{
			powerup = new PU_ExtraGuyPowerup(&spr_2uppowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1, 2);
			break;
		}
		case 3:
		{
			powerup = new PU_ExtraGuyPowerup(&spr_3uppowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1, 3);
			break;
		}
		case 4:
		{
			powerup = new PU_ExtraGuyPowerup(&spr_5uppowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1, 5);
			break;
		}
		case 5:
		{
			powerup = new PU_FirePowerup(&spr_firepowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1);
			break;
		}
		case 6:
		{
			powerup = new PU_StarPowerup(&spr_starpowerup, iSpawnX, iSpawnY, 4, side, 2, 30, 30, 1, 1);
			break;
		}
		case 7:
		{
			powerup = new PU_ClockPowerup(&spr_clockpowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1);
			break;
		}
		case 8:
		{
			powerup = new PU_BobombPowerup(&spr_bobombpowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1);
			break;
		}
		case 9:
		{
			powerup = new PU_PowPowerup(&spr_powpowerup, iSpawnX, iSpawnY, 8, side, 8, 30, 30, 1, 1);
			break;
		}
		case 10:
		{
			powerup = new PU_BulletBillPowerup(&spr_bulletbillpowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1);
			break;
		}
		case 11:
		{
			powerup = new PU_HammerPowerup(&spr_hammerpowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1);
			break;
		}
		case 12:
		{
			shell = new CO_Shell(0, iSpawnX, iSpawnY, true, true, true, false);
			break;
		}
		case 13:
		{
			shell = new CO_Shell(1, iSpawnX, iSpawnY, false, true, true, false);
			break;
		}
		case 14:
		{
			shell = new CO_Shell(2, iSpawnX, iSpawnY, false, false, true, true);
			break;
		}
		case 15:
		{
			shell = new CO_Shell(3, iSpawnX, iSpawnY, false, true, false, false);
			break;
		}
		case 16:
		{
			powerup = new PU_ModPowerup(&spr_modpowerup, iSpawnX, iSpawnY, 8, side, 8, 30, 30, 1, 1);
			break;
		}
		case 17:
		{
			feather = new PU_FeatherPowerup(&spr_featherpowerup, iSpawnX, iSpawnY, 1, 0, 30, 30, 1, 1);
			break;
		}
		case 18:
		{
			powerup = new PU_MysteryMushroomPowerup(&spr_mysterymushroompowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 30, 1, 1);
			break;
		}
		case 19:
		{
			powerup = new PU_BoomerangPowerup(&spr_boomerangpowerup, iSpawnX, iSpawnY, 1, side, 0, 30, 26, 1, 5);
			break;
		}
		case 20:
		{
			powerup = new PU_Tanooki(iSpawnX, iSpawnY);
			break;
		}
		case 21:
		{
			powerup = new PU_IceWandPowerup(&spr_icewandpowerup, iSpawnX, iSpawnY, 1, 0, 30, 30, 1, 1);
			break;
		}
		case 22:
		{
			powerup = new PU_PodoboPowerup(&spr_podobopowerup, iSpawnX, iSpawnY, 1, 0, 30, 30, 1, 1);
			break;
		}
		case 23:
		{
			powerup = new PU_BombPowerup(&spr_bombpowerup, iSpawnX, iSpawnY, 1, 0, 30, 30, 1, 1);
			break;
		}
		case 24:
		{
			feather = new PU_LeafPowerup(&spr_leafpowerup, iSpawnX, iSpawnY, 1, 0, 30, 30, 1, 1);
			break;
		}
		case 25:
		{
			powerup = new PU_PWingsPowerup(&spr_pwingspowerup, iSpawnX, iSpawnY);
			break;
		}
	}

	if(coin)
	{
		ifsoundonplay(sfx_coin);
		objectcontainer[1].add(coin);
		return coin;
	}
	else if(powerup)
	{
		objectcontainer[0].add(powerup);

		if(!spawn)
		{
			powerup->nospawn(iy);
			powerup->collision_detection_checksides();
		}

		return powerup;
	}
	else if(shell)
	{
		objectcontainer[1].add(shell);

		if(!spawn)
		{
			shell->nospawn(iy, true);
			shell->collision_detection_checksides();
		}

		return shell;
	}
	else if(feather)
	{
		objectcontainer[0].add(feather);

		if(!spawn)
			feather->nospawn(iy);

		return feather;
	}
	else //If no powerups were selected for this block, then fire out a podobo
	{
		IO_MovingObject * podobo = new MO_Podobo(&spr_podobo, ix + 2, iy, -(float(rand() % 5) / 2.0f) - 6.0f, -1, -1, -1, true);
		objectcontainer[2].add(podobo);
		return podobo;
	}

	return NULL;
}

//------------------------------------------------------------------------------
// class Block base class
//------------------------------------------------------------------------------

IO_Block::IO_Block(gfxSprite *nspr, short x, short y) :
	CObject(nspr, x, y)
{
	objectType = object_block;

	iBumpPlayerID = -1;
	iBumpTeamID = -1;

	fposx = fx;
	fposy = fy;

	iposx = x;
	iposy = y;

	col = x / TILESIZE;
	row = y / TILESIZE;

	hidden = ishiddentype = false;
	iHiddenTimer = 0;
}


void IO_Block::draw()
{
	spr->draw(ix, iy);
}


void IO_Block::update()
{
	if(ishiddentype && !hidden)
	{
		if(game_values.hiddenblockrespawn > 0 && ++iHiddenTimer > game_values.hiddenblockrespawn)
		{
			iHiddenTimer = 0;
			hidden = true;
			reset();

			g_map.UpdateTileGap(col, row);
		}
	}
}

void IO_Block::reset()
{
	state = 0;
}

bool IO_Block::collide(CPlayer * player, short direction, bool useBehavior)
{
	if(direction == 2)
		return hittop(player, useBehavior);
	else if(direction == 0)
		return hitbottom(player, useBehavior);
	else if(direction == 1)
		return hitleft(player, useBehavior);
	else
		return hitright(player, useBehavior);
}

bool IO_Block::hittop(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->yf((float)(iposy - PH) - 0.2f);
		player->inair = false;
		player->fallthrough = false;
		player->killsinrowinair = 0;
		player->extrajumps = 0;
		player->vely = GRAVITATION;
	}

	return false;
}

bool IO_Block::hitbottom(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		//Player bounces off 
		player->vely = CapFallingVelocity(-player->vely * BOUNCESTRENGTH);
		player->yf((float)(iposy + ih) + 0.2f);
	}

	return false;
}

bool IO_Block::hitright(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->xf((float)(iposx + iw) + 0.2f);
		player->fOldX = player->fx;
		
		if(player->velx < 0.0f)
			player->velx = 0.0f;

		if(player->oldvelx < 0.0f)
			player->oldvelx = 0.0f;
	}

	return false;
}

bool IO_Block::hitleft(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->xf((float)(iposx - PW) - 0.2f);
		player->fOldX = player->fx;
		
		if(player->velx > 0.0f)
			player->velx = 0.0f;

		if(player->oldvelx > 0.0f)
			player->oldvelx = 0.0f;
	}

	return false;
}

bool IO_Block::collide(IO_MovingObject * object, short direction)
{
	if(direction == 2)
		return hittop(object);
	else if(direction == 0)
		return hitbottom(object);
	else if(direction == 1)
		return hitleft(object);
	else
		return hitright(object);
}

bool IO_Block::hittop(IO_MovingObject * object)
{
	object->yf((float)(iposy - object->collisionHeight) - 0.2f);
	object->fOldY = object->fy;
	object->vely = object->BottomBounce();
	return true;
}

bool IO_Block::hitbottom(IO_MovingObject * object)
{
	object->yf((float)(iposy + ih) + 0.2f);
	object->fOldY = object->fy;
	object->vely = -object->vely;
	return true;
}

bool IO_Block::hitright(IO_MovingObject * object)
{
	object->xf((float)(iposx + iw) + 0.2f);
	object->fOldX = object->fx;

	if(object->velx < 0.0f)
		object->velx = -object->velx;

	return true;
}

bool IO_Block::hitleft(IO_MovingObject * object)
{
	object->xf((float)(iposx - object->collisionWidth) - 0.2f);
	object->fOldX = object->fx;
	
	if(object->velx > 0.0f)
		object->velx = -object->velx;

	return true;
}

void IO_Block::BounceMovingObject(IO_MovingObject * object)
{
	MovingObjectType type = object->getMovingObjectType();
	if(type == movingobject_goomba || type == movingobject_koopa || type == movingobject_buzzybeetle || type == movingobject_spiny)
	{
		ifsoundonplay(sfx_kicksound);
		
		MO_WalkingEnemy * enemy = (MO_WalkingEnemy *)object;
		killstyle style = enemy->getKillStyle();
		
		enemy->DieAndDropShell(true, true);

		if(!game_values.gamemode->gameover && iBumpPlayerID >= 0)
		{
			CPlayer * player = GetPlayerFromGlobalID(iBumpPlayerID);
			
			if(player)
			{
				AddAwardKill(player, NULL, style);
				player->score->AdjustScore(1);
			}
		}
	}
	else if(type == movingobject_shell)
	{
		CO_Shell * shell = (CO_Shell*)object;
		shell->Flip();
	}
	else if(type == movingobject_throwblock)
	{
		CO_ThrowBlock * block = (CO_ThrowBlock*)object;
		block->Die();
	}
	else if(type == movingobject_throwbox)
	{
		CO_ThrowBox * box = (CO_ThrowBox*)object;
		box->Die();
	}
	else
	{
		object->vely = -VELNOTEBLOCKREPEL;
	}
}

void IO_Block::KillPlayersAndObjectsInsideBlock(short playerID)
{
	//Loop through players
	for(short iPlayer = 0; iPlayer < list_players_cnt; iPlayer++)
	{
		CPlayer * player = list_players[iPlayer];

		if(!player->isready())
			continue;

		short iSwapSides = 0;
		if(player->fOldX >= iposx + TILESIZE)
			iSwapSides = -640;

		if(player->fOldX + PW + iSwapSides >= iposx && player->fOldX + iSwapSides < iposx + TILESIZE &&
			player->fOldY + PH >= iposy && player->fOldY < iposy + TILESIZE)
		{
			player->iSuicideCreditPlayerID = playerID;
			player->iSuicideCreditTimer = 1;
			player->KillPlayerMapHazard(true, kill_style_environment, true);
		}
	}

	//Loop through objects
	for(short iLayer = 0; iLayer < 3; iLayer++)
	{
		short iContainerEnd = objectcontainer[iLayer].list_end;
		for(short iObject = 0; iObject < iContainerEnd; iObject++)
		{
			CObject * object = objectcontainer[iLayer].list[iObject];

			if(object->getObjectType() != object_moving)
				continue;

			IO_MovingObject * movingobject = (IO_MovingObject*)object;

			if(!movingobject->CollidesWithMap())
				continue;

			short iSwapSides = 0;
			if(movingobject->fOldX >= iposx + TILESIZE)
				iSwapSides = -640;

			if(movingobject->fOldX + PW + iSwapSides >= iposx && movingobject->fOldX + iSwapSides < iposx + TILESIZE &&
				movingobject->fOldY + PH >= iposy && movingobject->fOldY < iposy + TILESIZE)
			{
				movingobject->KillObjectMapHazard(playerID);
			}
		}
	}
}

//------------------------------------------------------------------------------
// class powerup block
//------------------------------------------------------------------------------
B_PowerupBlock::B_PowerupBlock(gfxSprite *nspr1, short x, short y, short iNumSpr, short aniSpeed, bool fHidden, short * piSettings) :
	IO_Block(nspr1, x, y)
{
	iw = (short)spr->getWidth() >> 2;
	ih = (short)spr->getHeight() >> 1;  //This sprite has two images (unused and used blocks)
	collisionWidth = iw;
	timer = 0;
	side = true;
	iNumSprites = iNumSpr;
	animationSpeed = aniSpeed;
	animationTimer = 0;
	animationWidth = (short)spr->getWidth();
	drawFrame = 0;

	hidden = ishiddentype = fHidden;

	if(piSettings[0] == -1 || game_values.overridepowerupsettings == 1) //Game Only
	{
		for(short iSetting = 0; iSetting < NUM_POWERUPS; iSetting++)
			settings[iSetting] = game_values.powerupweights[iSetting];
	}
	else if(game_values.overridepowerupsettings == 0) //Map Only
	{
		for(short iSetting = 0; iSetting < NUM_POWERUPS; iSetting++)
			settings[iSetting] = piSettings[iSetting];
	}
	else if(game_values.overridepowerupsettings == 2) //Average
	{
		for(short iSetting = 0; iSetting < NUM_POWERUPS; iSetting++)
			settings[iSetting] = piSettings[iSetting] + game_values.powerupweights[iSetting];
	}
	else if(game_values.overridepowerupsettings == 3) //Weighted
	{
		float dMapWeightCount = 0;
		for(short iPowerup = 0; iPowerup < NUM_POWERUPS; iPowerup++)
			dMapWeightCount += piSettings[iPowerup];

		float dGameWeightCount = 0;
		for(short iPowerup = 0; iPowerup < NUM_POWERUPS; iPowerup++)
			dGameWeightCount += game_values.powerupweights[iPowerup];

		for(short iSetting = 0; iSetting < NUM_POWERUPS; iSetting++)
		{
			float dWeight = ((float)piSettings[iSetting] / dMapWeightCount + (float)game_values.powerupweights[iSetting] / dGameWeightCount) * 100.0f;

			//Cap lowest value at 1
			if(dWeight < 1.0f && dWeight > 0.0f)
				settings[iSetting] = 1;
			else
				settings[iSetting] = (short)dWeight;
		}
	}

	iCountWeight = 0;
	for(short iPowerup = 0; iPowerup < NUM_POWERUPS; iPowerup++)
		iCountWeight += settings[iPowerup];
}


void B_PowerupBlock::draw()
{
	if(!hidden)
		spr->draw(ix, iy, drawFrame, state == 0 ? 0 : ih, iw, ih);
}

void B_PowerupBlock::update()
{
	IO_Block::update();

	if(state > 0)
	{
		yf(fy + vely);
		
		if(state == 1 && fabsf(fposy - fy) > 10.0f)
		{
			vely = -vely;
			state = 2;
			iBumpPlayerID = -1;
		}
		else if(state == 2 && fabsf(fposy - fy) < VELBLOCKBOUNCE)
		{
			vely = 0.0f;
			state = 3;
			yi(iposy);

			if(game_values.gamemode->gamemode == game_mode_health && rand() % 100 < game_values.gamemodesettings.health.percentextralife)
			{
				createpowerup(HEALTH_POWERUP, ix, iy, side, true);
			}
			else if((game_values.gamemode->gamemode == game_mode_timelimit && rand() % 100 < game_values.gamemodesettings.time.percentextratime) || 
					(game_values.gamemode->gamemode == game_mode_star && rand() % 100 < game_values.gamemodesettings.star.percentextratime))
			{
				createpowerup(TIME_POWERUP, ix, iy, side, true);
			}
			else if((game_values.gamemode->gamemode == game_mode_coins && rand() % 100 < game_values.gamemodesettings.coins.percentextracoin) || 
					(game_values.gamemode->gamemode == game_mode_greed && rand() % 100 < game_values.gamemodesettings.greed.percentextracoin))
			{
				createpowerup(COIN_POWERUP, ix, iy, side, true);
			}
			else if(game_values.gamemode->gamemode == game_mode_jail && (rand() % 100) < game_values.gamemodesettings.jail.percentkey)
			{
				createpowerup(JAIL_KEY_POWERUP, ix, iy, side, true);
			}
			else
			{
				createpowerup(SelectPowerup(), ix, iy, side, true);
			}

			ifsoundonplay(sfx_sprout);
		}
		else if(state == 3)
		{
			if(game_values.itemrespawntime > 0 && ++timer >= game_values.itemrespawntime)
			{
				reset();
			}
		}
	}

	if(++animationTimer >= animationSpeed)
	{
		animationTimer = 0;

		drawFrame += iw;
		if(drawFrame >= animationWidth)
		{
			drawFrame = 0;
		}
	}
}

void B_PowerupBlock::reset()
{
	timer = 0;
	state = 0;
}

bool B_PowerupBlock::collide(CPlayer * player, short direction, bool useBehavior)
{
	if(hidden)
	{
		if(player->fOldY >= iposy + ih && direction == 0)
			return hitbottom(player, useBehavior);

		return true;
	}

	return IO_Block::collide(player, direction, useBehavior);
}

bool B_PowerupBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);

	if(state == 1)
	{
		short iKillType = player_kill_nonkill;
		if(iBumpPlayerID >= 0 && !player->IsInvincibleOnBottom() && (player->teamID != iBumpTeamID || game_values.teamcollision == 2))
			iKillType = PlayerKilledPlayer(iBumpPlayerID, player, death_style_jump, kill_style_bounce, false, false);
		
		if(player_kill_nonkill == iKillType)
			player->vely = -VELNOTEBLOCKREPEL;
	}
	else if(useBehavior)
	{
		player->vely = GRAVITATION;

		if(state == 0)
		{
			if(player->IsSuperStomping())
			{
				state = 1;
				vely = -VELBLOCKBOUNCE;
				side = player->ix + HALFPW < ix + (iw >> 1);
			}
		}
	}

	return false;
}

bool B_PowerupBlock::hitbottom(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		//Player bounces off 
		player->vely = CapFallingVelocity(-player->vely * BOUNCESTRENGTH);
		player->yf((float)(iposy + ih) + 0.2f);

		if(hidden)
		{
			hidden = false;
			KillPlayersAndObjectsInsideBlock(player->globalID);
		}

		g_map.UpdateTileGap(col, row);

		if(state == 0)
		{
			ifsoundonplay(sfx_bump);

			iBumpPlayerID = player->globalID;
			iBumpTeamID = player->teamID;

			vely = -VELBLOCKBOUNCE;
			state = 1;
			side = player->ix + HALFPW < ix + (iw >> 1);
		}
	}

	return false;
}

bool B_PowerupBlock::collide(IO_MovingObject * object, short direction)
{
	if(hidden)
		return true;

	return IO_Block::collide(object, direction);
}

bool B_PowerupBlock::hittop(IO_MovingObject * object)
{
	object->yf((float)(iposy - object->collisionHeight) - 0.2f);
	object->fOldY = object->fy;
	
	MovingObjectType type = object->getMovingObjectType();
	if(type == movingobject_throwbox && ((CO_ThrowBox*)object)->HasKillVelocity())
	{
		if(state == 0)
		{
			ifsoundonplay(sfx_bump);

			iBumpPlayerID = -1;
			vely = -VELBLOCKBOUNCE;
			state = 1;
			side = false;
		}

		object->vely = object->BottomBounce();
	}
	else if(state == 1 && object->bounce == GRAVITATION)
	{
		BounceMovingObject(object);
		return false;
	}
	else
	{
		object->vely = object->BottomBounce();
	}

	return true;
}

bool B_PowerupBlock::hitright(IO_MovingObject * object)
{
	//Object bounces off
	object->xf((float)(iposx + iw) + 0.2f);
	object->fOldX = object->fx;

	if(object->velx < 0.0f)
		object->velx = -object->velx;

	MovingObjectType type = object->getMovingObjectType();
	if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_attackzone)
	{
		if(type == movingobject_shell)
		{
			if(object->state != 1)
				return false;
		}

		//If the box isn't moving fast enough, then don't do anything
		if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
			return false;

		if(state == 0)
		{
			ifsoundonplay(sfx_bump);

			iBumpPlayerID = -1;
			vely = -VELBLOCKBOUNCE;
			state = 1;
			side = false;
		}

		return true;
	}

	return false;
}

bool B_PowerupBlock::hitleft(IO_MovingObject * object)
{
	//Object bounces off
	object->xf((float)(iposx - object->collisionWidth) - 0.2f);
	object->fOldX = object->fx;

	if(object->velx > 0.0f)
		object->velx = -object->velx;

	MovingObjectType type = object->getMovingObjectType();
	if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_attackzone)
	{
		if(type == movingobject_shell)
		{
			if(object->state != 1)
				return false;
		}

		//If the box isn't moving fast enough, then don't do anything
		if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
			return false;

		if(state == 0)
		{
			ifsoundonplay(sfx_bump);

			iBumpPlayerID = -1;
			vely = -VELBLOCKBOUNCE;
			state = 1;
			side = true;
		}

		return true;
	}

	return false;
}

void B_PowerupBlock::triggerBehavior()
{
	if(state == 0)
	{
		ifsoundonplay(sfx_bump);

		iBumpPlayerID = -1;
		vely = -VELBLOCKBOUNCE;
		state = 1;
		side = true;
	}
}

short B_PowerupBlock::SelectPowerup()
{
	if(iCountWeight == 0)
		return -1;

	int iRandPowerup = rand() % iCountWeight + 1;
	int iSelectedPowerup = 0;

	int iPowerupWeightCount = settings[iSelectedPowerup];

	while(iPowerupWeightCount < iRandPowerup)
		iPowerupWeightCount += settings[++iSelectedPowerup];

	return iSelectedPowerup;
}

//------------------------------------------------------------------------------
// class view powerup block
//------------------------------------------------------------------------------
B_ViewBlock::B_ViewBlock(gfxSprite *nspr1, short x, short y, bool fHidden, short * piSettings) :
	B_PowerupBlock(nspr1, x, y, 1, 32000, fHidden, piSettings)
{
	poweruptimer = 0;
	powerupindex = rand() % NUM_POWERUPS;

	iw = 32;
	ih = 32;

	iCountWeight = 0;
	for(short iPowerup = 0; iPowerup < NUM_POWERUPS; iPowerup++)
		iCountWeight += settings[iPowerup];

	fNoPowerupsSelected = iCountWeight == 0;
	GetNextPowerup();
}


void B_ViewBlock::draw()
{
	if(hidden)
		return;

	//Draw powerup behind block
	if(state == 0 && !fNoPowerupsSelected)
		spr_storedpoweruplarge.draw(ix, iy, powerupindex * 32, 0, 32, 32);

	B_PowerupBlock::draw();
}

void B_ViewBlock::update()
{
	B_PowerupBlock::update();

	if(state == 0 && !fNoPowerupsSelected)
	{
		if(++poweruptimer > settings[powerupindex] * 10)
		{
			poweruptimer = 0;
			GetNextPowerup();
		}
	}
}

short B_ViewBlock::SelectPowerup()
{
	if(fNoPowerupsSelected)
		return -1;

	return powerupindex;
}

void B_ViewBlock::GetNextPowerup()
{
	if(fNoPowerupsSelected)
		return;

	int iRandPowerup = rand() % iCountWeight + 1;
	powerupindex = 0;
	int iPowerupWeightCount = settings[powerupindex];

	while(iPowerupWeightCount < iRandPowerup)
		iPowerupWeightCount += settings[++powerupindex];
}

//------------------------------------------------------------------------------
// class breakable block
//------------------------------------------------------------------------------
B_BreakableBlock::B_BreakableBlock(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed) :
	IO_Block(nspr, x, y)
{
	iw = (short)nspr->getWidth() >> 2;
	iNumSprites = iNumSpr;
	animationSpeed = aniSpeed;
	animationTimer = 0;
	animationWidth = (short)nspr->getWidth();
	drawFrame = 0;
}

void B_BreakableBlock::draw()
{
	if(state == 0)
		spr->draw(ix, iy, drawFrame, 0, iw, ih);
}

void B_BreakableBlock::update()
{
	if(state > 0)
	{
		if(state == 1)
		{
			state = 2;
		}
		else if(state == 2)
		{
			iBumpPlayerID = -1;
			dead = true;
			g_map.blockdata[col][row] = NULL;
			g_map.UpdateTileGap(col, row);
		}
	}

	if(++animationTimer >= animationSpeed)
	{
		animationTimer = 0;

		drawFrame += iw;
		if(drawFrame >= animationWidth)
		{
			drawFrame = 0;
		}
	}
}


bool B_BreakableBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);

	if(state == 1 || state == 2)
	{
		short iKillType = player_kill_nonkill;
		if(iBumpPlayerID >= 0 && !player->IsInvincibleOnBottom() && (player->teamID != iBumpTeamID || game_values.teamcollision == 2))
			iKillType = PlayerKilledPlayer(iBumpPlayerID, player, death_style_jump, kill_style_bounce, false, false);
		
		if(player_kill_nonkill == iKillType)
			player->vely = -VELNOTEBLOCKREPEL;
	}
	else if(useBehavior)
	{
		player->vely = GRAVITATION;

		if(player->IsSuperStomping() && state == 0)
		{
			triggerBehavior();
			return true;
		}
	}

	return false;
}

bool B_BreakableBlock::hitbottom(CPlayer * player, bool useBehavior)
{
	if(useBehavior && state == 0)
	{
		triggerBehavior();
		
		//When breaking a block, you smash through with a small velocity, but this allows for breaking two blocks at once
		/*
		if(player->vely < -VELMAXBREAKBLOCK)
			player->vely = -VELMAXBREAKBLOCK;
		*/
		player->vely = CapFallingVelocity(-player->vely * BOUNCESTRENGTH);
		player->yf((float)(iposy + ih) + 0.2f);
		
		iBumpPlayerID = player->globalID;
		iBumpTeamID = player->teamID;
	}

	return false;
}

bool B_BreakableBlock::hittop(IO_MovingObject * object)
{
	object->yf((float)(iposy - object->collisionHeight) - 0.2f);
	object->fOldY = object->fy;
	
	if(state == 0)
	{
		MovingObjectType type = object->getMovingObjectType();
		if(type == movingobject_throwbox && ((CO_ThrowBox*)object)->HasKillVelocity())
		{
			triggerBehavior();
			object->vely = object->BottomBounce();
			return true;
		}
	}

	if((state == 1  || state == 2) && object->bounce == GRAVITATION)
	{
		BounceMovingObject(object);
		return false;
	}
	else
	{
		object->vely = object->BottomBounce();
	}

	return true;
}

bool B_BreakableBlock::hitright(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->xf((float)(iposx + iw) + 0.2f);
		object->fOldX = object->fx;

		if(object->velx < 0.0f)
			object->velx = -object->velx;

		MovingObjectType type = object->getMovingObjectType();
		if((type == movingobject_shell && object->state == 1) || type == movingobject_throwblock || (type == movingobject_throwbox && ((CO_ThrowBox*)object)->HasKillVelocity()) || type == movingobject_attackzone)
		{
			triggerBehavior();
			return true;
		}
	}

	return false;
}

bool B_BreakableBlock::hitleft(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->xf((float)(iposx - object->collisionWidth) - 0.2f);
		object->fOldX = object->fx;
		
		if(object->velx > 0.0f)
			object->velx = -object->velx;

		MovingObjectType type = object->getMovingObjectType();
		if((type == movingobject_shell && object->state == 1) || type == movingobject_throwblock || (type == movingobject_throwbox && ((CO_ThrowBox*)object)->HasKillVelocity()) || type == movingobject_attackzone)
		{
			triggerBehavior();	
			return true;
		}
	}

	return false;
}

void B_BreakableBlock::triggerBehavior()
{
	if(state == 0)
	{
		eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix, iy, -2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
		eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix + 16, iy, 2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
		eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix, iy + 16, -2.2f, -5.5f, 4, 2, 0, 0, 16, 16));
		eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix + 16, iy + 16, 2.2f, -5.5f, 4, 2, 0, 0, 16, 16));

		state = 1;
		ifsoundonplay(sfx_breakblock);
	}

	game_values.unlocksecret1part2++;
	CheckSecret(0);
}

//------------------------------------------------------------------------------
// class note block
//------------------------------------------------------------------------------
B_NoteBlock::B_NoteBlock(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short type, bool fHidden) :
	IO_Block(nspr, x, y)
{
	iw = (short)nspr->getWidth() >> 2;
	ih = TILESIZE;
	iNumSprites = iNumSpr;
	animationSpeed = aniSpeed;
	animationTimer = 0;
	drawFrame = 0;
	animationWidth = (short)spr->getWidth();

	hidden = ishiddentype = fHidden;

	iType = type;
	iTypeOffsetY = iType << 5;
}

void B_NoteBlock::draw()
{
	if(hidden)
		return;

	spr->draw(ix, iy, drawFrame, iTypeOffsetY, iw, ih);
}

void B_NoteBlock::update()
{
	IO_Block::update();

	if(state > 0)
	{
		xf(fx + velx);
		yf(fy + vely);

		if(state == 1 && fabsf(fposx - fx) > 10.0f)
		{
			velx = -velx;
			state = 2;
		}
		else if(state == 2 && fabsf(fposx - fx) < VELNOTEBLOCKBOUNCE)
		{
			reset();
		}
		else if(state == 3 && fabsf(fposy - fy) > 10.0f)
		{
			vely = -vely;
			state = 4;
			iBumpPlayerID = -1;
		}
		else if(state == 4 && fabsf(fposy - fy) < VELNOTEBLOCKBOUNCE)
		{
			reset();
		}
	}

	if(++animationTimer >= animationSpeed)
	{
		animationTimer = 0;

		drawFrame += iw;
		if(drawFrame >= animationWidth)
		{
			drawFrame = 0;
		}
	}
}

void B_NoteBlock::reset()
{
	velx = 0.0f;
	vely = 0.0f;
	state = 0;
	xf(fposx);
	yf(fposy);
}

bool B_NoteBlock::collide(CPlayer * player, short direction, bool useBehavior)
{
	if(hidden)
	{
		if((player->fOldY >= iposy + ih || state > 1) && direction == 0)
			return hitbottom(player, useBehavior);

		return true;
	}

	return IO_Block::collide(player, direction, useBehavior);
}

bool B_NoteBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);

	if(useBehavior)
	{
		player->superjumptimer = 4;
		player->superjumptype = iType;
		player->vely = -VELNOTEBLOCKREPEL;
	
		if(state == 0)
		{
			vely = VELNOTEBLOCKBOUNCE;
			state = 3;
		}

		ifsoundonplay(sfx_bump);

		game_values.unlocksecret3part2[player->globalID] += 2;
		CheckSecret(2);
	}
	
	return false;
}

bool B_NoteBlock::hitbottom(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->yf((float)(iposy + ih) + 0.2f);
		player->vely = VELNOTEBLOCKREPEL;

		if(state == 0)
		{
			iBumpPlayerID = player->globalID;
			iBumpTeamID = player->teamID;

			vely = -VELNOTEBLOCKBOUNCE;
			state = 3;

			ifsoundonplay(sfx_bump);
		}

		if(hidden)
		{
			hidden = false;
			KillPlayersAndObjectsInsideBlock(player->globalID);
		}

		g_map.UpdateTileGap(col, row);
	}

	return false;
}

bool B_NoteBlock::hitright(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->xf((float)(iposx + iw) + 0.2f);
		player->fOldX = player->fx;
		player->velx = VELNOTEBLOCKREPEL;
		player->oldvelx = VELNOTEBLOCKREPEL;

		if(state == 0)
		{
			velx = -VELNOTEBLOCKBOUNCE;
			state = 1;
		}

		ifsoundonplay(sfx_bump);
	}

	return false;
}

bool B_NoteBlock::hitleft(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->xf((float)(iposx - PW) - 0.2f);
		player->fOldX = player->fx;
		player->velx = -VELNOTEBLOCKREPEL;
		player->oldvelx = -VELNOTEBLOCKREPEL;

		if(state == 0)
		{
			velx = VELNOTEBLOCKBOUNCE;
			state = 1;
		}

		ifsoundonplay(sfx_bump);
	}

	return false;
}

bool B_NoteBlock::collide(IO_MovingObject * object, short direction)
{
	if(hidden)
		return true;

	return IO_Block::collide(object, direction);
}

bool B_NoteBlock::hittop(IO_MovingObject * object)
{
	object->yf((float)(iposy - object->collisionHeight) - 0.2f);
	object->fOldY = object->fy;
	
	if(state == 3 && object->bounce == GRAVITATION)
	{
		BounceMovingObject(object);
		return false;
	}
	else
	{
		object->vely = object->BottomBounce();
	}

	return true;
}

//------------------------------------------------------------------------------
// class donut block
//------------------------------------------------------------------------------
B_DonutBlock::B_DonutBlock(gfxSprite *nspr, short x, short y) :
	IO_Block(nspr, x, y)
{
	counter = 0;
	jigglex = 0;
	jigglecounter = 0;
}


void B_DonutBlock::draw()
{
	spr->draw(ix + jigglex, iy);
}


void B_DonutBlock::update()
{
	//If a player is standing on us, jiggle and then fall
	if(state == 0)
	{
		counter = 0;
		jigglex = 0;
		jigglecounter = 0;
	}
	else
	{
		if(++jigglecounter > 1)
		{
			jigglecounter = 0;

			if(jigglex == 2)
				jigglex = -2;
			else
				jigglex = 2;
		}

		if(++counter > 50)
		{
			triggerBehavior(state - 1);
		}
	}

	state = 0;
}

bool B_DonutBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);

	if(useBehavior)
		state = player->globalID + 1;
	
	return false;
}

void B_DonutBlock::triggerBehavior(short iPlayerId)
{
	//eyecandy[2].add(new EC_FallingObject(&spr_donutblock, ix, iy, 0.0f, 0, 0, 0, 0));
	
	TilesetTile ** tiledata = new TilesetTile*[1];
	tiledata[0] = new TilesetTile[1];
	tiledata[0][0].iID = g_tilesetmanager.GetClassicTilesetIndex();
	tiledata[0][0].iCol = 29;
	tiledata[0][0].iRow = 15;

	MapTile ** typedata = new MapTile*[1];
	typedata[0] = new MapTile[1];
	typedata[0][0].iType = tile_solid;
	typedata[0][0].iFlags = tile_flag_solid;

	MovingPlatformPath * path = new FallingPath((float)ix + 16.0f, (float)iy + 15.8f); 
	MovingPlatform * platform = new MovingPlatform(tiledata, typedata, 1, 1, 2, path, false);
	platform->SetPlayerId(iPlayerId);

	g_map.AddTemporaryPlatform(platform);

	dead = true;
	g_map.blockdata[col][row] = NULL;
	g_map.UpdateTileGap(col, row);
}

//------------------------------------------------------------------------------
// class flip block
// Known Bug:  If you have two flip blocks vertically aligned like this:
//
// ['']  or  ['']['']  etc.
// ['']      ['']['']
//
// And you hit both of them from the bottom, then keep jumping so that the bottom one
// stops to support you, and then the top one stops spinning, you can "hide" under the
// top block.  I was thinking about fixing this, but I don't know what the correct 
// behavior should be if you're in a flip block when it stops spinning.  Plus, it is kind
// of cool to hide under these blocks.  You are somewhat invincible, but someone can come 
// along and hit your support out from under you and you'll fall out and if they have star
// power, you're dead.  Plus fireballs have a large enough collision box that you can be hit
// with them too.
//------------------------------------------------------------------------------
B_FlipBlock::B_FlipBlock(gfxSprite *nspr, short x, short y, bool fHidden) :
	IO_Block(nspr, x, y)
{
	iw = (short)spr->getWidth() >> 2;
	collisionWidth = iw;

	hidden = ishiddentype = fHidden;

	counter = 0;
	frame = 0;
	timer = 0;
	animationWidth = (short)spr->getWidth();
}

void B_FlipBlock::draw()
{
	if(hidden)
		return;

	if(state == 0 || state == 1)
		spr->draw(ix, iy, frame, 0, iw, ih);
}

void B_FlipBlock::update()
{
	IO_Block::update();

	if(state == 1)
	{
		if(++counter >= 10)
		{
			counter = 0;
			frame += iw;

			if(frame >= animationWidth)
			{
				frame = 0;
			}
		}

		if(++timer >= 240)
		{
			reset();
			g_map.UpdateTileGap(col, row);

			KillPlayersAndObjectsInsideBlock(iBumpPlayerID);
			iBumpPlayerID = -1;
		}
	}
	else if(state == 2)
	{
		state = 3;
	}
	else if(state == 3)
	{
		dead = true;
		g_map.blockdata[col][row] = NULL;
		g_map.UpdateTileGap(col, row);
	}
}

void B_FlipBlock::reset()
{
	frame = 0;
	counter = 0;
	timer = 0;
	state = 0;
}

bool B_FlipBlock::collide(CPlayer * player, short direction, bool useBehavior)
{
	if(hidden)
	{
		if(player->fOldY >= iposy + ih && direction == 0)
			return hitbottom(player, useBehavior);

		return true;
	}

	if((player->fOldY + PH <= iposy || state > 1) && direction == 2)
		return hittop(player, useBehavior);
	else if((player->fOldY >= iposy + ih || state > 1) && direction == 0)
		return hitbottom(player, useBehavior);
	else if((player->fOldX + PW <= iposx || state > 1) && direction == 1)
		return hitleft(player, useBehavior);
	else if((player->fOldX >= iposx + iw || state > 1) && direction == 3)
		return hitright(player, useBehavior);

	return true;
}

bool B_FlipBlock::hittop(CPlayer * player, bool useBehavior)
{
	if(state == 2 || state == 3)
	{
		IO_Block::hittop(player, useBehavior);

		player->vely = -VELNOTEBLOCKREPEL;
		return false;
	}
	else if(state == 0)
	{
		IO_Block::hittop(player, useBehavior);

		if(player->IsSuperStomping())
		{
			state = 2;
			explode();
			return true;
		}

		return false;
	}

	return true;
}

bool B_FlipBlock::hitbottom(CPlayer * player, bool useBehavior)
{
	if(useBehavior && state == 0)
	{
		player->vely = CapFallingVelocity(-player->vely * BOUNCESTRENGTH);
		player->yf((float)(iposy + ih) + 0.2f);
		
		iBumpPlayerID = player->globalID;
		iBumpTeamID = player->teamID;

		if(hidden)
		{
			hidden = false;
			KillPlayersAndObjectsInsideBlock(iBumpPlayerID);
		}

		g_map.UpdateTileGap(col, row);

		triggerBehavior();
		return false;
	}

	return true;
}

bool B_FlipBlock::hitright(CPlayer * player, bool useBehavior)
{
	if(useBehavior && state == 0)
	{
		IO_Block::hitright(player, useBehavior);
	}
	
	return true;
}

bool B_FlipBlock::hitleft(CPlayer * player, bool useBehavior)
{
	if(useBehavior && state == 0)
	{
		IO_Block::hitleft(player, useBehavior);
	}

	return true;
}

bool B_FlipBlock::collide(IO_MovingObject * object, short direction)
{
	if(hidden)
		return true;

	return IO_Block::collide(object, direction);
}

bool B_FlipBlock::hittop(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->yf((float)(iposy - object->collisionHeight) - 0.2f);
		object->fOldY = object->fy;
		object->vely = object->BottomBounce();
	}

	return true;
}

bool B_FlipBlock::hitbottom(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->yf((float)(iposy + ih) + 0.2f);
		object->vely = -object->vely;
	}
	
	return true;
}

bool B_FlipBlock::hitright(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->xf((float)(iposx + iw) + 0.2f);
		object->fOldX = object->fx;

		if(object->velx < 0.0f)
			object->velx = -object->velx;

		MovingObjectType type = object->getMovingObjectType();
		if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox)
		{
			if(type == movingobject_shell)
			{
				if(object->state != 1)
					return false;
			}

			if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
			{
				return false;
			}

			state = 2;
			explode();
		}
		else if(type == movingobject_attackzone)
		{
			iBumpPlayerID = object->iPlayerID;
			iBumpTeamID = object->iTeamID;

			if(hidden)
			{
				hidden = false;
				KillPlayersAndObjectsInsideBlock(iBumpPlayerID);
			}

			g_map.UpdateTileGap(col, row);

			triggerBehavior();
			return false;
		}
	}

	return true;
}

bool B_FlipBlock::hitleft(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->xf((float)(iposx - object->collisionWidth) - 0.2f);
		object->fOldX = object->fx;
		
		if(object->velx > 0.0f)
			object->velx = -object->velx;

		MovingObjectType type = object->getMovingObjectType();
		if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox)
		{
			if(type == movingobject_shell)
			{
				if(object->state != 1)
					return false;
			}

			if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
			{
				return false;
			}

			state = 2;
			explode();
		}
		else if(type == movingobject_attackzone)
		{
			iBumpPlayerID = object->iPlayerID;
			iBumpTeamID = object->iTeamID;

			if(hidden)
			{
				hidden = false;
				KillPlayersAndObjectsInsideBlock(iBumpPlayerID);
			}

			g_map.UpdateTileGap(col, row);

			triggerBehavior();
			return false;
		}
	}

	return true;
}

void B_FlipBlock::triggerBehavior()
{
	if(state == 0)
	{
		state = 1;
		frame = iw;

		g_map.UpdateTileGap(col, row);
	}
}

void B_FlipBlock::explode()
{
	eyecandy[2].add(new EC_FallingObject(&spr_brokenflipblock, ix, iy, -2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenflipblock, ix + 16, iy, 2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenflipblock, ix, iy + 16, -2.2f, -5.5f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenflipblock, ix + 16, iy + 16, 2.2f, -5.5f, 4, 2, 0, 0, 16, 16));
	
	ifsoundonplay(sfx_breakblock);
}

//------------------------------------------------------------------------------
// class On/Off Switch block
//------------------------------------------------------------------------------
B_OnOffSwitchBlock::B_OnOffSwitchBlock(gfxSprite *nspr, short x, short y, short colorID, short iState) :
	IO_Block(nspr, x, y)
{
	iw = (short)spr->getWidth() >> 2;
	collisionWidth = iw;
	ih = (short)spr->getHeight() >> 2;
	collisionHeight = ih;

	iColorID = colorID;
	iSrcX = colorID * 32;

	state = (iState == 0 ? 3 : 0);
}

void B_OnOffSwitchBlock::update()
{
	if(state != 0 && state != 3)
	{
		yf(fy + vely);

		if((state == 1 || state == 4) && fabsf(fposy - fy) > 10.0f)
		{
			iBumpPlayerID = -1;
			vely = -vely;
			state++;
		}
		else if((state == 2 || state == 5) && fabsf(fposy - fy) < VELBLOCKBOUNCE)
		{
			vely = 0.0f;
			state -= 2;
			yf(fposy);
		}
	}
}

void B_OnOffSwitchBlock::draw()
{
	spr->draw(ix, iy, iSrcX, (state == 0 ? 0 : 32), iw, ih);
}

bool B_OnOffSwitchBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);

	if(state == 1 || state == 4)
	{
		short iKillType = player_kill_nonkill;
		if(iBumpPlayerID >= 0 && !player->IsInvincibleOnBottom() && (player->teamID != iBumpTeamID || game_values.teamcollision == 2))
			iKillType = PlayerKilledPlayer(iBumpPlayerID, player, death_style_jump, kill_style_bounce, false, false);
		
		if(player_kill_nonkill == iKillType)
			player->vely = -VELNOTEBLOCKREPEL;
	}
	else if(useBehavior)
	{
		player->vely = GRAVITATION;

		if((state == 0 || state == 3) && player->IsSuperStomping())
			triggerBehavior(player->globalID);
	}

	return false;
}

bool B_OnOffSwitchBlock::hitbottom(CPlayer * player, bool useBehavior)
{
	//Player bounces off 
	if(useBehavior)
	{
		player->vely = CapFallingVelocity(-player->vely * BOUNCESTRENGTH);
		player->yf((float)(iposy + ih) + 0.2f);

		if(state == 0 || state == 3)
		{
			iBumpPlayerID = player->globalID;
			iBumpTeamID = player->teamID;

			triggerBehavior(iBumpPlayerID);
		}
	}

	return false;
}

bool B_OnOffSwitchBlock::hittop(IO_MovingObject * object)
{
	object->yf((float)(iposy - object->collisionHeight) - 0.2f);
	object->fOldY = object->fy;

	MovingObjectType type = object->getMovingObjectType();
	if(type == movingobject_throwbox && ((CO_ThrowBox*)object)->HasKillVelocity())
	{
		if(state == 0 || state == 3)
		{
			iBumpPlayerID = -1;
			triggerBehavior(((IO_MovingObject*)object)->iPlayerID);
		}

		object->vely = object->BottomBounce();

		return true;
	}
	else if((state == 1 || state == 4) && object->bounce == GRAVITATION)
	{
		BounceMovingObject(object);
		return false;
	}
	else
	{
		object->vely = object->BottomBounce();
	}

	return true;
}

bool B_OnOffSwitchBlock::hitright(IO_MovingObject * object)
{
	//Object bounces off
	object->xf((float)(iposx + iw) + 0.2f);
	object->fOldX = object->fx;

	if(object->velx < 0.0f)
		object->velx = -object->velx;

	MovingObjectType type = object->getMovingObjectType();
	if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_attackzone)
	{
		if(type == movingobject_shell)
		{
			if(object->state != 1)
				return false;
		}

		if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
		{
			return false;
		}

		if(state == 0 || state == 3)
		{
			iBumpPlayerID = -1;
			triggerBehavior(((IO_MovingObject*)object)->iPlayerID);
		}
		else
		{
			ifsoundonplay(sfx_bump);
		}

		return true;
	}

	return false;
}

bool B_OnOffSwitchBlock::hitleft(IO_MovingObject * object)
{
	//Object bounces off
	object->xf((float)(iposx - object->collisionWidth) - 0.2f);
	object->fOldX = object->fx;

	if(object->velx > 0.0f)
		object->velx = -object->velx;

	MovingObjectType type = object->getMovingObjectType();
	if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_attackzone)
	{
		if(type == movingobject_shell)
		{
			if(object->state != 1)
				return false;
		}

		if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
		{
			return false;
		}

		if(state == 0 || state == 3)
		{
			iBumpPlayerID = -1;
			triggerBehavior(((IO_MovingObject*)object)->iPlayerID);
		}
		else
		{
			ifsoundonplay(sfx_bump);
		}

		return true;
	}

	return false;
}

void B_OnOffSwitchBlock::triggerBehavior(short playerID)
{
	if(state == 0 || state == 3)
	{
		ifsoundonplay(sfx_switchpress);
		vely = -VELBLOCKBOUNCE;
		
		state++;
		
		//Switch all the switch blocks and all the on/off blocks of the same color
		std::list<IO_Block*>::iterator iterateSwitches = g_map.switchBlocks[iColorID].begin();

		while (iterateSwitches != g_map.switchBlocks[iColorID].end())
		{
			((B_OnOffSwitchBlock*)(*iterateSwitches))->FlipState();
			iterateSwitches++;
		}

		//Switch all the switch blocks
		iterateSwitches = g_map.switchBlocks[iColorID + 4].begin();

		while (iterateSwitches != g_map.switchBlocks[iColorID + 4].end())
		{
			((B_SwitchBlock*)(*iterateSwitches))->FlipState(playerID);
			iterateSwitches++;
		}
	}
}

//------------------------------------------------------------------------------
// class switch block
//------------------------------------------------------------------------------

B_SwitchBlock::B_SwitchBlock(gfxSprite *nspr, short x, short y, short colorID, short iState) :
	IO_Block(nspr, x, y)
{
	iw = (short)spr->getWidth() >> 2;
	collisionWidth = iw;
	ih = (short)spr->getHeight() >> 2;
	collisionHeight = ih;

	state = 1 - iState;
	iSrcX = colorID * 32;
}

void B_SwitchBlock::draw()
{
	spr->draw(ix, iy, iSrcX, (state == 0 ? 64 : 96), iw, ih);
}

bool B_SwitchBlock::collide(CPlayer * player, short direction, bool useBehavior)
{
	if(state != 0 || !useBehavior)
		return true;

	if(player->fOldY + PH <= iposy && direction == 2)
		return hittop(player, useBehavior);
	else if(player->fOldY >= iposy + ih && direction == 0)
		return hitbottom(player, useBehavior);
	else if(player->fOldX + PW <= iposx && direction == 1)
		return hitleft(player, useBehavior);
	else if(player->fOldX >= iposx + iw && direction == 3)
		return hitright(player, useBehavior);

	return true;
}

bool B_SwitchBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);
	return false;
}

bool B_SwitchBlock::hitbottom(CPlayer * player, bool)
{
	player->vely = CapFallingVelocity(-player->vely * BOUNCESTRENGTH);
	player->yf((float)(iposy + ih) + 0.2f);
	vely = -VELBLOCKBOUNCE;
	return false;
}

bool B_SwitchBlock::hitright(CPlayer * player, bool)
{
	player->xf((float)(iposx + iw) + 0.2f);
	player->fOldX = player->fx;

	if(player->velx < 0.0f)
		player->velx = 0.0f;

	if(player->oldvelx < 0.0f)
		player->oldvelx = 0.0f;

	return false;
}

bool B_SwitchBlock::hitleft(CPlayer * player, bool)
{
	player->xf((float)(iposx - PW) - 0.2f);
	player->fOldX = player->fx;

	if(player->velx > 0.0f)
		player->velx = 0.0f;

	if(player->oldvelx > 0.0f)
		player->oldvelx = 0.0f;

	return false;
}

bool B_SwitchBlock::hittop(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->yf((float)(iposy - object->collisionHeight) - 0.2f);
		object->fOldY = object->fy;
		object->vely = object->BottomBounce();
	}

	return true;
}

bool B_SwitchBlock::hitbottom(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->yf((float)(iposy + ih) + 0.2f);
		object->vely = -object->vely;
	}
	
	return true;
}

bool B_SwitchBlock::hitright(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->xf((float)(iposx + iw) + 0.2f);

		if(object->velx < 0.0f)
			object->velx = -object->velx;
	}

	return true;
}

bool B_SwitchBlock::hitleft(IO_MovingObject * object)
{
	if(state == 0)
	{
		object->xf((float)(iposx - object->collisionWidth) - 0.2f);
		
		if(object->velx > 0.0f)
			object->velx = -object->velx;
	}

	return true;
}

void B_SwitchBlock::FlipState(short playerID)
{
	state = 1 - state;
	g_map.UpdateTileGap(col, row);

	if(state == 0)
		KillPlayersAndObjectsInsideBlock(playerID);
}

//------------------------------------------------------------------------------
// class bounce block
//------------------------------------------------------------------------------
B_BounceBlock::B_BounceBlock(gfxSprite *nspr1, short x, short y, bool fHidden) :
	IO_Block(nspr1, x, y)
{
	hidden = ishiddentype = fHidden;
}

void B_BounceBlock::draw()
{
	if(hidden)
		return;

	IO_Block::draw();
}

void B_BounceBlock::update()
{
	IO_Block::update();

	if(state > 0)
	{
		yf(fy + vely);

		if(state == 1 && fabsf(fposy - fy) > 10.0f)
		{
			iBumpPlayerID = -1;
			vely = -vely;
			state = 2;
		}
		else if(state == 2 && fabsf(fposy - fy) < VELBLOCKBOUNCE)
		{
			reset();
		}
	}
}

void B_BounceBlock::reset()
{
	vely = 0.0f;
	state = 0;
	yf(fposy);
}

bool B_BounceBlock::collide(CPlayer * player, short direction, bool useBehavior)
{
	if(hidden)
	{
		if(player->fOldY >= iposy + ih && direction == 0)
			return hitbottom(player, useBehavior);

		return true;
	}

	return IO_Block::collide(player, direction, useBehavior);
}

bool B_BounceBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);

	if(state == 1)
	{
		short iKillType = player_kill_nonkill;
		if(iBumpPlayerID >= 0 && !player->IsInvincibleOnBottom() && (player->teamID != iBumpTeamID || game_values.teamcollision == 2))
			iKillType = PlayerKilledPlayer(iBumpPlayerID, player, death_style_jump, kill_style_bounce, false, false);
		
		if(player_kill_nonkill == iKillType)
			player->vely = -VELNOTEBLOCKREPEL;
	}
	else if(useBehavior)
	{
		player->vely = GRAVITATION;
	}

	return false;
}

bool B_BounceBlock::hitbottom(CPlayer * player, bool useBehavior)
{
	//Player bounces off 
	if(useBehavior)
	{
		player->vely = CapFallingVelocity(-player->vely * BOUNCESTRENGTH);
		player->yf((float)(iposy + ih) + 0.2f);

		iBumpPlayerID = player->globalID;
		iBumpTeamID = player->teamID;

		triggerBehavior();

		if(hidden)
		{
			hidden = false;
			KillPlayersAndObjectsInsideBlock(iBumpPlayerID);
		}

		g_map.UpdateTileGap(col, row);
	}

	return false;
}

bool B_BounceBlock::collide(IO_MovingObject * object, short direction)
{
	if(hidden)
		return true;

	return IO_Block::collide(object, direction);
}

bool B_BounceBlock::hittop(IO_MovingObject * object)
{
	object->yf((float)(iposy - object->collisionHeight) - 0.2f);
	object->fOldY = object->fy;
	
	if(state == 1 && object->bounce == GRAVITATION)
	{
		BounceMovingObject(object);
		return false;
	}
	else
	{
		object->vely = object->BottomBounce();
	}

	return true;
}

void B_BounceBlock::triggerBehavior()
{
	if(state == 0)
	{
		vely = -VELBLOCKBOUNCE;
		state = 1;
		ifsoundonplay(sfx_bump);
	}
}

//------------------------------------------------------------------------------
// class throw block
//------------------------------------------------------------------------------
B_ThrowBlock::B_ThrowBlock(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short type) :
	IO_Block(nspr, x, y)
{
	iw = 32;
	ih = 32;
	iNumSprites = iNumSpr;
	animationSpeed = aniSpeed;
	animationTimer = 0;
	drawFrame = 0;
	animationWidth = (short)nspr->getWidth();
	iType = type;
}

void B_ThrowBlock::draw()
{
	spr->draw(ix, iy, drawFrame, iType << 5, iw, ih);
}

void B_ThrowBlock::update()
{
	if(++animationTimer >= animationSpeed)
	{
		animationTimer = 0;

		drawFrame += iw;
		if(drawFrame >= animationWidth)
		{
			drawFrame = 0;
		}
	}
}

bool B_ThrowBlock::hittop(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->yf((float)(iposy - PH) - 0.2f);
		player->fOldY = player->fy;
		player->inair = false;
		player->fallthrough = false;
		player->killsinrowinair = 0;
		player->extrajumps = 0;
		player->vely = GRAVITATION;

		if(player->PressedAcceptItemKey() && player->IsAcceptingItem())
		{
			GiveBlockToPlayer(player);
			return true;
		}
		else
		{
			return false;
		}
	}

	return false;
}

bool B_ThrowBlock::hitright(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->xf((float)(iposx + iw) + 0.2f);
		player->fOldX = player->fx;

		if(player->velx < 0.0f)
			player->velx = 0.0f;

		if(player->oldvelx < 0.0f)
			player->oldvelx = 0.0f;

		if(player->IsAcceptingItem())
		{
			GiveBlockToPlayer(player);
			return true;
		}
		else
		{
			return false;
		}
	}
	
	return true;
}

bool B_ThrowBlock::hitleft(CPlayer * player, bool useBehavior)
{
	if(useBehavior)
	{
		player->xf((float)(iposx - PW) - 0.2f);
		player->fOldX = player->fx;

		if(player->velx > 0.0f)
			player->velx = 0.0f;

		if(player->oldvelx > 0.0f)
			player->oldvelx = 0.0f;

		if(player->IsAcceptingItem())
		{
			GiveBlockToPlayer(player);
			return true;
		}
		else
		{
			return false;
		}
	}
	
	return true;
}

void B_ThrowBlock::GiveBlockToPlayer(CPlayer * player)
{
	CO_ThrowBlock * block = new CO_ThrowBlock(&spr_blueblock, ix, iy, iType);
	if(player->AcceptItem(block))
	{
		dead = true;
		g_map.blockdata[col][row] = NULL;
		g_map.UpdateTileGap(col, row);

		block->owner = player;
		block->iPlayerID = player->globalID;
		objectcontainer[1].add(block);
	}
	else
	{
		delete block;
	}
}

void B_ThrowBlock::triggerBehavior()
{
	dead = true;
	g_map.blockdata[col][row] = NULL;
	g_map.UpdateTileGap(col, row);

	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix, iy, -1.5f, -7.0f, 6, 2, 0, iType << 4, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix + 16, iy, 1.5f, -7.0f, 6, 2, 0, iType << 4, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix, iy + 16, -1.5f, -4.0f, 6, 2, 0, iType << 4, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix + 16, iy + 16, 1.5f, -4.0f, 6, 2, 0, iType << 4, 16, 16));
	
	ifsoundonplay(sfx_breakblock);
}


//------------------------------------------------------------------------------
// class weapon breakable block
//------------------------------------------------------------------------------
B_WeaponBreakableBlock::B_WeaponBreakableBlock(gfxSprite *nspr, short x, short y, short type) :
	IO_Block(nspr, x, y)
{
	iType = type;
	iw = TILESIZE;
	ih = TILESIZE;

	iDrawOffsetX = type << 5;
}

void B_WeaponBreakableBlock::draw()
{
	if(state == 0)
		spr->draw(ix, iy, iDrawOffsetX, 0, iw, ih);
}

void B_WeaponBreakableBlock::update()
{
	if(state > 0)
	{
		if(state == 1)
		{
			state = 2;
		}
		else if(state == 2)
		{
			iBumpPlayerID = -1;
			dead = true;
			g_map.blockdata[col][row] = NULL;
			g_map.UpdateTileGap(col, row);
		}
	}
}


bool B_WeaponBreakableBlock::hittop(CPlayer * player, bool useBehavior)
{
	IO_Block::hittop(player, useBehavior);

	if(state == 1 || state == 2)
	{
		short iKillType = player_kill_nonkill;
		if(iBumpPlayerID >= 0 && !player->IsInvincibleOnBottom() && (player->teamID != iBumpTeamID || game_values.teamcollision == 2))
			PlayerKilledPlayer(iBumpPlayerID, player, death_style_jump, kill_style_bounce, false, false);
		
		if(player_kill_nonkill == iKillType)
			player->vely = -VELNOTEBLOCKREPEL;
	}
	else if(useBehavior)
	{
		player->vely = GRAVITATION;

		//Save this for when we create a super stomp destroyable block
		if(iType == 6 && player->IsSuperStomping() && state == 0)
		{
			triggerBehavior(player->globalID, player->teamID);
			return false;
		}
		else if(iType == 8 && player->invincible)
		{
			triggerBehavior(player->globalID, player->teamID);
			return false;
		}
	}

	return false;
}

bool B_WeaponBreakableBlock::hitbottom(CPlayer * player, bool useBehavior)
{
	if(useBehavior && state == 0)
	{
		//If the player has a cape and they used it for a feather destroyable block, then kill it
		bool fTriggerBlock = false;
		if(iType == 1 && player->powerup == 3 && player->extrajumps > 0)
		{
			fTriggerBlock = true;
		}
		else if(iType == 7 && player->powerup == 8 && player->flying)
		{
			fTriggerBlock = true;
		}
		else if(iType == 8 && player->invincible)
		{
			fTriggerBlock = true;
		}
		
		if(fTriggerBlock)
			triggerBehavior(player->globalID, player->teamID);
	
		return IO_Block::hitbottom(player, useBehavior);
	}

	return false;
}

bool B_WeaponBreakableBlock::hitleft(CPlayer * player, bool useBehavior)
{
	if(useBehavior && state == 0)
	{
		if(iType == 8 && player->invincible)
			triggerBehavior(player->globalID, player->teamID);

		return IO_Block::hitleft(player, useBehavior);
	}
	
	return false;
}

bool B_WeaponBreakableBlock::hitright(CPlayer * player, bool useBehavior)
{
	if(useBehavior && state == 0)
	{
		if(iType == 8 && player->invincible)
			triggerBehavior(player->globalID, player->teamID);

		return IO_Block::hitright(player, useBehavior);
	}
	
	return false;
}

bool B_WeaponBreakableBlock::hittop(IO_MovingObject * object)
{
	IO_Block::hittop(object);
	
	if((state == 1  || state == 2) && object->bounce == GRAVITATION)
	{
		BounceMovingObject(object);
		return false;
	}

	if(state != 0)
		return true;

	MovingObjectType type = object->getMovingObjectType();

	if(iType == 0 && type == movingobject_fireball)
	{
		triggerBehavior(object->iPlayerID, object->iTeamID);
		removeifprojectile(object, false, true);
		return false;
	}

	return true;
}

bool B_WeaponBreakableBlock::hitbottom(IO_MovingObject * object)
{
	if(state != 0)
		return true;

	IO_Block::hitbottom(object);
	
	MovingObjectType type = object->getMovingObjectType();

	if(iType == 0 && type == movingobject_fireball)
	{
		triggerBehavior(object->iPlayerID, object->iTeamID);
		removeifprojectile(object, false, true);
		return false;
	}

	return true;
}

bool B_WeaponBreakableBlock::hitright(IO_MovingObject * object)
{
	if(state != 0)
		return true;

	IO_Block::hitright(object);

	return objecthitside(object);
}

bool B_WeaponBreakableBlock::hitleft(IO_MovingObject * object)
{
	if(state != 0)
		return true;

	IO_Block::hitleft(object);

	return objecthitside(object);
}

void B_WeaponBreakableBlock::triggerBehavior(short iPlayerID, short iTeamID)
{
	if(state == 0)
	{
		eyecandy[2].add(new EC_FallingObject(&spr_brokengrayblock, ix, iy, -2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
		eyecandy[2].add(new EC_FallingObject(&spr_brokengrayblock, ix + 16, iy, 2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
		eyecandy[2].add(new EC_FallingObject(&spr_brokengrayblock, ix, iy + 16, -2.2f, -5.5f, 4, 2, 0, 0, 16, 16));
		eyecandy[2].add(new EC_FallingObject(&spr_brokengrayblock, ix + 16, iy + 16, 2.2f, -5.5f, 4, 2, 0, 0, 16, 16));

		state = 1;
		ifsoundonplay(sfx_breakblock);

		iBumpPlayerID = iPlayerID;
		iBumpTeamID = iTeamID;
	}
}

bool B_WeaponBreakableBlock::objecthitside(IO_MovingObject * object)
{
	MovingObjectType type = object->getMovingObjectType();
	
	if(iType == 2 && ((type == movingobject_shell && object->state == 1) || type == movingobject_throwblock || (type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())))
	{
		short iPlayerID = -1;
		short iTeamID = -1;
		if(type == movingobject_shell)
		{
			CO_Shell * shell = (CO_Shell*)object;
			iPlayerID = shell->iPlayerID;
			iTeamID = shell->iTeamID;
		}
		else if(type == movingobject_throwblock)
		{
			CO_ThrowBlock * throwblock = (CO_ThrowBlock*)object;
			iPlayerID = throwblock->iPlayerID;
			iTeamID = throwblock->iTeamID;
		}
		else if(type == movingobject_throwbox)
		{
			CO_ThrowBox * throwbox = (CO_ThrowBox*)object;
			iPlayerID = throwbox->iPlayerID;
			iTeamID = throwbox->iTeamID;
		}

		triggerBehavior(iPlayerID, iTeamID);
		return false;
	}
	else if(iType == 0 && type == movingobject_fireball)
	{
		MO_Fireball * fireball = (MO_Fireball*)object;
		triggerBehavior(fireball->iPlayerID, fireball->iTeamID);
		removeifprojectile(object, false, true);
		return false;
	}
	else if(type == movingobject_attackzone)
	{
		MO_AttackZone * zone = (MO_AttackZone*)object;

		if((zone->iStyle == kill_style_leaf && iType == 9) || (zone->iStyle == kill_style_feather && iType == 1))
		{
			triggerBehavior(zone->iPlayerID, zone->iTeamID);
			zone->Die();
			return false;
		}
	}

	return true;
}


//------------------------------------------------------------------------------
// class powerup
//------------------------------------------------------------------------------
MO_Powerup::MO_Powerup(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	desty = fy - collisionHeight;
	movingObjectType = movingobject_powerup;
}

void MO_Powerup::draw()
{
	if(state == 0)
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, 0, iw, (short)(ih - fy + desty));
	else
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, 0, iw, ih);
}


void MO_Powerup::update()
{
	//Have the powerup grow out of the powerup block
	if(state == 0)
	{
		yf(fy - 2.0f);

		if(fy <= desty)
		{
			fy = desty;
			state = 1;
			vely = 1.0f;
		}
	}
	else //Then have it obey the physics
	{
		fOldX = fx;
		fOldY = fy;

		collision_detection_map();
	}
	
	animate();
}

bool MO_Powerup::collide(CPlayer *)
{
	if(state > 0)
		dead = true;

	return false;
}

void MO_Powerup::nospawn(short y)
{
	state = 1;
	yi(y);
	vely = -VELJUMP / 2.0;
}

//------------------------------------------------------------------------------
// tanooki suit
//------------------------------------------------------------------------------
PU_Tanooki::PU_Tanooki(short x, short y)
    : MO_Powerup(&spr_tanooki, x, y, 1, 0, 30, 30, 1, 1)
{
}

bool PU_Tanooki :: collide (CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(9);
		dead = true;
	}

    return false;
}

//------------------------------------------------------------------------------
// pwings
//------------------------------------------------------------------------------
PU_PWingsPowerup::PU_PWingsPowerup(gfxSprite * nspr, short x, short y)
    : MO_Powerup(nspr, x, y, 1, 0, 30, 30, 1, 1)
{}

bool PU_PWingsPowerup::collide (CPlayer *player)
{
	player->SetPowerup(8);
	dead = true;
	return false;
}


//------------------------------------------------------------------------------
// class star powerup
//------------------------------------------------------------------------------
PU_StarPowerup::PU_StarPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool moveToRight, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	if(moveToRight)
		velx = 2.0f;
	else
		velx = -2.0f;

	bounce = -VELPOWERUPBOUNCE;
}

bool PU_StarPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		dead = true;

		if(!player->invincible && !player->shyguy)
		{
			player->makeinvincible();
		}
		else
		{
			player->SetStoredPowerup(6);
		}
	}

	return false;
}

//------------------------------------------------------------------------------
// class 1up powerup
//------------------------------------------------------------------------------
PU_ExtraGuyPowerup::PU_ExtraGuyPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool moveToRight, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY, short type) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	if(moveToRight)
		velx = 1.0f + (float)type;
	else
		velx = -1.0f - (float)type;

	iType = type;
}

bool PU_ExtraGuyPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		game_values.gamemode->playerextraguy(*player, iType);
		ifsoundonplay(sfx_extraguysound);

		eyecandy[2].add(new EC_FloatingObject(&spr_extralife, player->ix + HALFPW - 19, player->iy - 16, 0.0f, -1.5f, 62, player->colorID * 38, (iType == 5 ? 3 : iType - 1) * 16, 38, 16));
		
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// class poison powerup
//------------------------------------------------------------------------------
PU_PoisonPowerup::PU_PoisonPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool moveToRight, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	if(moveToRight)
		velx = 2.0f;
	else
		velx = -2.0f;

	movingObjectType = movingobject_poisonpowerup;
}

bool PU_PoisonPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		if(player->invincible || player->shyguy)
		{
			dead = true;
			return false;
		}

		if(player->shield > 0)
			return false;

		dead = true;

		return player->KillPlayerMapHazard(false, kill_style_poisonmushroom, false) != player_kill_nonkill;
	}

	return false;
}

//------------------------------------------------------------------------------
// class mystery mushroom powerup
//------------------------------------------------------------------------------
PU_MysteryMushroomPowerup::PU_MysteryMushroomPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool moveToRight, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	if(moveToRight)
		velx = 2.0f;
	else
		velx = -2.0f;
}

bool PU_MysteryMushroomPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		dead = true;

		if(!SwapPlayers(player->localID))
		{
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, player->ix + (HALFPW) - 16, player->iy + (HALFPH) - 16, 3, 8));
			ifsoundonplay(sfx_spit);
		}
	}

	return false;
}

//------------------------------------------------------------------------------
// class fire powerup
//------------------------------------------------------------------------------
PU_FirePowerup::PU_FirePowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_FirePowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(1);
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// class hammer powerup
//------------------------------------------------------------------------------
PU_HammerPowerup::PU_HammerPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_HammerPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(2);
		dead = true;
	}

	return false;
}	

//------------------------------------------------------------------------------
// class ice wand powerup
//------------------------------------------------------------------------------
PU_IceWandPowerup::PU_IceWandPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;
}

void PU_IceWandPowerup::update()
{
	MO_Powerup::update();

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}
}

void PU_IceWandPowerup::draw()
{
	MO_Powerup::draw();

	//Draw sparkles
	if(state == 1)
		spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
}

bool PU_IceWandPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(5);
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// class podobo powerup
//------------------------------------------------------------------------------
PU_PodoboPowerup::PU_PodoboPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_PodoboPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		dead = true;
		player->SetStoredPowerup(22);
	}

	return false;
}

//------------------------------------------------------------------------------
// class bomb powerup
//------------------------------------------------------------------------------
PU_BombPowerup::PU_BombPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_BombPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(6);
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// secret powerup
//------------------------------------------------------------------------------
PU_SecretPowerup::PU_SecretPowerup(gfxSprite * nspr, short x, short y, short type)
    : MO_Powerup(nspr, x, y, 4, 8, 30, 30, 1, 1)
{
	itemtype = type;
	place();
}

void PU_SecretPowerup::update()
{
	MO_Powerup::update();

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}
}

void PU_SecretPowerup::draw()
{
	MO_Powerup::draw();

	//Draw sparkles
	spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
}

bool PU_SecretPowerup::collide (CPlayer *player)
{
	if(itemtype == 0)
	{
		game_values.windaffectsplayers = true;
		
		for(short i = 0; i < 15; i++)
			eyecandy[2].add(new EC_Snow(&spr_snow, (float)(rand() % 640), (float)(rand() % 480), rand() % 4 + 1));
	}
	else if(itemtype == 1)
	{
		game_values.spinscreen = true;
	}
	else if(itemtype == 2)
	{
		game_values.reversewalk = true;
	}
	else if(itemtype == 3)
	{
		game_values.spotlights = true;
	}

	ifsoundonplay(sfx_pickup);

	dead = true;
	return false;
}

void PU_SecretPowerup::place()
{
	short iAttempts = 10;
	while(!g_map.findspawnpoint(5, &ix, &iy, collisionWidth, collisionHeight, false) && iAttempts-- > 0);

	fx = (float)ix;
	fy = (float)iy;
}


//------------------------------------------------------------------------------
// class treasure chest powerup
//------------------------------------------------------------------------------
PU_TreasureChestBonus::PU_TreasureChestBonus(gfxSprite *nspr, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY, short iBonusItem) :
	MO_Powerup(nspr, 0, 0, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;
	bounce = -VELPOWERUPBOUNCE * 2;
	numbounces = 5;
	state = 2;
	bonusitem = iBonusItem;
	
	short iAttempts = 10;
	while(!g_map.findspawnpoint(5, &ix, &iy, collisionWidth, collisionHeight, false) && iAttempts-- > 0);
	fx = (float)ix;
	fy = (float)iy;

	drawbonusitemx = 0;
	drawbonusitemy = 0;
	drawbonusitemtimer = 0;

	fObjectDiesOnSuperDeathTiles = false;
}

void PU_TreasureChestBonus::update()
{
	MO_Powerup::update();

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}

	//Draw rising powerup from chest
	if (state == 3)
	{
		drawbonusitemy -= 2;

		if(--drawbonusitemtimer <= 0)
			state = 4;
	}
	else if (state == 4)
	{
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix, drawbonusitemy, 3, 8));
		dead = true;
	}
}

void PU_TreasureChestBonus::draw()
{
	if(state < 3)
	{
		MO_Powerup::draw();

		//Draw sparkles
		spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
	}
	else
	{
		if(bonusitem >= NUM_POWERUPS)
			spr_worlditems.draw(drawbonusitemx, drawbonusitemy, (bonusitem - NUM_POWERUPS) << 5, 0, 32, 32);
		else
			spr_storedpoweruplarge.draw(drawbonusitemx, drawbonusitemy, bonusitem << 5, 0, 32, 32);
	}
}

bool PU_TreasureChestBonus::collide(CPlayer * player)
{
	if(state == 1)
	{
		ifsoundonplay(sfx_treasurechest);
		//if(game_values.worldpowerupcount[player->teamID] < 32)
        //    game_values.worldpowerups[player->teamID][game_values.worldpowerupcount[player->teamID]++] = bonusitem;
		//else
		//	game_values.worldpowerups[player->teamID][31] = bonusitem;

		state = 3;

		drawbonusitemx = ix;
		drawbonusitemy = iy;
		drawbonusitemtimer = 60;

		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix, iy, 3, 8));

		game_values.noexit = false;
	}

	return false;
}

float PU_TreasureChestBonus::BottomBounce()
{
	if(state == 2)
	{
		if(--numbounces <= 0)
		{
			numbounces = 0;
			state = 1;
			bounce = GRAVITATION;
		}
		else
		{
			if(vely > 0.0f)
				bounce = -vely / 2.0f;
			else
				bounce /= 2.0f;
		}
	}

	return bounce;
}


//------------------------------------------------------------------------------
// class treasure chest powerup
//------------------------------------------------------------------------------
MO_BonusHouseChest::MO_BonusHouseChest(gfxSprite *nspr, short ix, short iy, short iBonusItem) :
	IO_MovingObject(nspr, ix, iy, 1, 0, 64, 64, 0, 0)
{
	iw = 64;
	ih = 64;

	state = 1;
	bonusitem = iBonusItem;

	drawbonusitemy = 0;
	drawbonusitemtimer = 0;

	movingObjectType = movingobject_treasurechest;

	fObjectDiesOnSuperDeathTiles = false;
	fObjectCollidesWithMap = false;
}

void MO_BonusHouseChest::update()
{
	//Draw rising powerup from chest
	if (state == 2)
	{
		drawbonusitemy -= 2;

		if(--drawbonusitemtimer <= 0)
		{
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix + 16, drawbonusitemy, 3, 8));
			state = 3;
		}
	}
}

void MO_BonusHouseChest::draw()
{
	if(state < 2)
		spr->draw(ix, iy, 0, 0, iw, ih);

	if(state >= 2)
		spr->draw(ix, iy, 128, 0, iw, ih);

	if(state == 2)
	{
		if(bonusitem >= NUM_POWERUPS + NUM_WORLD_POWERUPS) //Score bonuses
		{
			short iBonus = bonusitem - NUM_POWERUPS - NUM_WORLD_POWERUPS;
			short iBonusX = (iBonus % 10) << 5;
			short iBonusY = ((iBonus / 10) << 5) + 32;
			spr_worlditems.draw(ix + 16, drawbonusitemy, iBonusX, iBonusY, 32, 32);
		}
		else if(bonusitem >= NUM_POWERUPS) //World Item
			spr_worlditems.draw(ix + 16, drawbonusitemy, (bonusitem - NUM_POWERUPS) << 5, 0, 32, 32);
		else //Normal Powerup
			spr_storedpoweruplarge.draw(ix + 16, drawbonusitemy, bonusitem << 5, 0, 32, 32);
	}

	if(state >= 2)
		spr->draw(ix, iy, 64, 0, iw, ih);
}

bool MO_BonusHouseChest::collide(CPlayer * player)
{
	if(state == 1 && !game_values.gamemode->gameover && player->playerKeys->game_turbo.fPressed)
	{
		if(bonusitem < NUM_POWERUPS + NUM_WORLD_POWERUPS)
		{
			if(game_values.worldpowerupcount[player->teamID] < 32)
				game_values.worldpowerups[player->teamID][game_values.worldpowerupcount[player->teamID]++] = bonusitem;
			else
				game_values.worldpowerups[player->teamID][31] = bonusitem;
		}
		else
		{
			short iBonus = bonusitem - NUM_POWERUPS - NUM_WORLD_POWERUPS;
			if(iBonus < 10)
				iBonus = iBonus + 1;
			else
				iBonus = 9 - iBonus;

			game_values.tournament_scores[player->teamID].total += iBonus;

			if(game_values.tournament_scores[player->teamID].total < 0)
				game_values.tournament_scores[player->teamID].total = 0;
		}

		ifsoundonplay(sfx_treasurechest);
		state = 2;

		drawbonusitemy = iy + 32;
		drawbonusitemtimer = 75;

		game_values.forceexittimer = 180;
		game_values.gamemode->gameover = true;
		game_values.gamemode->winningteam = player->teamID;
	}

	return false;
}


//------------------------------------------------------------------------------
// class clock powerup
//------------------------------------------------------------------------------
PU_ClockPowerup::PU_ClockPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_ClockPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetStoredPowerup(7);
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// class bobomb powerup
//------------------------------------------------------------------------------
PU_BobombPowerup::PU_BobombPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_BobombPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(0);
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// class POW powerup
//------------------------------------------------------------------------------
PU_PowPowerup::PU_PowPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_PowPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		dead = true;
		player->SetStoredPowerup(9);
	}

	return false;
}

//------------------------------------------------------------------------------
// class MOd powerup
//------------------------------------------------------------------------------
PU_ModPowerup::PU_ModPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_ModPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		dead = true;
		player->SetStoredPowerup(16);
	}

	return false;
}

//------------------------------------------------------------------------------
// class BulletBill powerup
//------------------------------------------------------------------------------
PU_BulletBillPowerup::PU_BulletBillPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_BulletBillPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		dead = true;
		player->SetStoredPowerup(10);
	}

	return false;
}

//------------------------------------------------------------------------------
// class feather powerup
//------------------------------------------------------------------------------
PU_FeatherPowerup::PU_FeatherPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	desty = fy - collisionHeight;
	movingObjectType = movingobject_powerup;

	iw = (short)nspr->getWidth() >> 1;

	velx = 0.0f;
	fFloatDirectionRight = true;
	dFloatAngle = HALF_PI;

	fObjectCollidesWithMap = false;
}

void PU_FeatherPowerup::draw()
{
	if(state == 0)
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, 0, iw, (short)(ih - fy + desty));
	else if(state == 1)
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, 0, iw, ih);
	else
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, (fFloatDirectionRight ? 0 : 32), 0, iw, ih);
}


void PU_FeatherPowerup::update()
{
	//Have the powerup grow out of the powerup block
	if(state == 0)
	{
		yf(fy - 4.0f);

		if(fy <= desty)
		{
			state = 1;
		}
	}
	else if(state == 1)
	{
		fOldX = fx;
		fOldY = fy;

		yf(fy - 4.0f);
		
		if(fy <= desty - 128.0f)
		{
			state = 2;
			dFloatCenterY = fy - 64.0f;
			dFloatCenterX = fx;
		}
	}
	else
	{
		if(!fFloatDirectionRight)
		{
			dFloatAngle += 0.035f;

			if(dFloatAngle >= THREE_QUARTER_PI)
			{
				dFloatAngle = THREE_QUARTER_PI;
				fFloatDirectionRight = true;
			}
		}
		else
		{
			dFloatAngle -= 0.035f;

			if(dFloatAngle <= QUARTER_PI)
			{
				dFloatAngle = QUARTER_PI;
				fFloatDirectionRight = false;
			}
		}

		dFloatCenterY += 1.0f;

		xf(64.0f * cos(dFloatAngle) + dFloatCenterX);
		yf(64.0f * sin(dFloatAngle) + dFloatCenterY);

		if(fy >= 480.0f)
			dead = true;
	}
}

bool PU_FeatherPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(3);
		dead = true;
	}

	return false;
}

void PU_FeatherPowerup::nospawn(short y)
{
	state = 1;
	desty = y;
	yi(y + TILESIZE - collisionHeight);
}

//------------------------------------------------------------------------------
// class leaf powerup
//------------------------------------------------------------------------------
PU_LeafPowerup::PU_LeafPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	PU_FeatherPowerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{}

bool PU_LeafPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(7);
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// class boomerang powerup
//------------------------------------------------------------------------------
PU_BoomerangPowerup::PU_BoomerangPowerup(gfxSprite *nspr, short x, short y, short iNumSpr, bool, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	MO_Powerup(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	velx = 0.0f;
}

bool PU_BoomerangPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		player->SetPowerup(4);
		dead = true;
	}

	return false;
}	


//------------------------------------------------------------------------------
// class special heart powerup for health mode
//------------------------------------------------------------------------------
PU_ExtraHeartPowerup::PU_ExtraHeartPowerup(gfxSprite *nspr, short x, short y) :
	MO_Powerup(nspr, x, y, 1, 0, 30, 30, 1, 1)
{
	velx = 0.0f;
}

bool PU_ExtraHeartPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		if(game_values.gamemode->gamemode == game_mode_health)
		{
			if(player->score->subscore[1] < game_values.gamemodesettings.health.maxlife)
				player->score->subscore[1]++;

			if(player->score->subscore[0] < game_values.gamemodesettings.health.maxlife)
				player->score->subscore[0]++;
		}

		ifsoundonplay(sfx_collectpowerup);
		dead = true;
	}

	return false;
}

//------------------------------------------------------------------------------
// class special extra time powerup for timed or star mode
//------------------------------------------------------------------------------
PU_ExtraTimePowerup::PU_ExtraTimePowerup(gfxSprite *nspr, short x, short y) :
	MO_Powerup(nspr, x, y, 1, 0, 30, 30, 1, 1)
{
	velx = 0.0f;
}

bool PU_ExtraTimePowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		if(game_values.gamemode->gamemode == game_mode_timelimit || game_values.gamemode->gamemode == game_mode_star)
		{
			CGM_TimeLimit * timelimitmode = (CGM_TimeLimit*)game_values.gamemode;
			timelimitmode->addtime(timelimitmode->goal / 5);
		}

		ifsoundonplay(sfx_collectpowerup);
		dead = true;
	}

	return false;
}


//------------------------------------------------------------------------------
// class special extra coin powerup for coin or greed mode
//------------------------------------------------------------------------------
PU_CoinPowerup::PU_CoinPowerup(gfxSprite *nspr, short x, short y, short color, short value) :
	MO_Powerup(nspr, x, y, 4, 8, 30, 30, 1, 1)
{
	velx = 0.0f;
	iColorOffsetY = color << 5;
	iValue = value;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;
}

void PU_CoinPowerup::update()
{
	MO_Powerup::update();

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}
}

void PU_CoinPowerup::draw()
{
	if(state == 0)
	{
		short iHeight = (short)(32 - fy + desty);
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iColorOffsetY, 32, iHeight);
		spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, iHeight);
	}
	else
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iColorOffsetY, 32, 32);
		spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
	}
}

bool PU_CoinPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		if(game_values.gamemode->gamemode == game_mode_coins || game_values.gamemode->gamemode == game_mode_greed)
		{
			if(!game_values.gamemode->gameover)
			{
				player->score->AdjustScore(iValue);
				game_values.gamemode->CheckWinner(player);
			}
		}

		ifsoundonplay(sfx_coin);
		dead = true;
	}

	return false;
}



//------------------------------------------------------------------------------
// class special jail key powerup for jail mode
//------------------------------------------------------------------------------
PU_JailKeyPowerup::PU_JailKeyPowerup(gfxSprite *nspr, short x, short y) :
	MO_Powerup(nspr, x, y, 1, 0, 30, 30, 1, 1)
{
	velx = 0.0f;
}

bool PU_JailKeyPowerup::collide(CPlayer * player)
{
	if(state > 0)
	{
		dead = true;
		player->SetStoredPowerup(26);
	}

	return false;
}

//------------------------------------------------------------------------------
// class fireball
//------------------------------------------------------------------------------
MO_Fireball::MO_Fireball(gfxSprite *nspr, short x, short y, short iNumSpr, bool moveToRight, short aniSpeed, short iGlobalID, short teamID, short iColorID) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, (short)nspr->getWidth() >> 2, (short)nspr->getHeight() >> 3, 0, 0)
{
	if(moveToRight)
		velx = 5.0f;
	else
		velx = -5.0f;

	//fireball sprites have both right and left sprites in them
	ih = ih >> 3;

	bounce = -FIREBALLBOUNCE;

	iPlayerID = iGlobalID;
	iTeamID = teamID;

	colorOffset = iColorID * 36;
	movingObjectType = movingobject_fireball;
	
	state = 1;

	ttl = game_values.fireballttl;

	sSpotlight = NULL;
}

void MO_Fireball::update()
{
	IO_MovingObject::update();

	if(--ttl <= 0)
	{
		removeifprojectile(this, true, true);
	}
	else if(game_values.spotlights)
	{
		if(!sSpotlight)
		{
			sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
		}

		if(sSpotlight)
		{
			sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
		}
	}
}

bool MO_Fireball::collide(CPlayer * player)
{
	if(iPlayerID != player->globalID && (game_values.teamcollision == 2|| iTeamID != player->teamID))
	{
		if(player->shield == 0)
		{
			removeifprojectile(this, false, false);

			if(!player->invincible && !player->shyguy)
			{
				//Find the player that shot this fireball so we can attribute a kill
				PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_fireball, false, false);
				return true;
			}
		}
	}

	return false;
}

void MO_Fireball::draw()
{
	spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, (velx > 0 ? 0 : 18) + colorOffset, iw, ih);
}


//------------------------------------------------------------------------------
// class super fireball
//------------------------------------------------------------------------------
MO_SuperFireball::MO_SuperFireball(gfxSprite *nspr, short x, short y, short iNumSpr, float fVelyX, float fVelyY, short aniSpeed, short iGlobalID, short teamID, short iColorID) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, nspr->getWidth() / iNumSpr, nspr->getHeight() / 10, 0, 0)
{
	ih /= 10;

	iPlayerID = iGlobalID;
	iTeamID = teamID;
	colorOffset = (iColorID + 1) * 64;
	directionOffset = velx < 0.0f ? 0 : 32;
	movingObjectType = movingobject_superfireball;
	
	state = 1;
	
	velx = fVelyX;
	vely = fVelyY;
	ttl = rand() % 30 + 60;
	
	drawframe = 0;

	fObjectCollidesWithMap = false;
}

void MO_SuperFireball::update()
{
	animate();

	xf(fx + velx);
	yf(fy + vely);
	
	if(ix < 0)
		xi(ix + 640);
	else if(ix > 639)
		xi(ix - 640);
	
	if(iy > 480 || iy < -ih || --ttl <= 0)
		removeifprojectile(this, false, true);
}

bool MO_SuperFireball::collide(CPlayer * player)
{
	if(player->shield == 0)
	{
		dead = true;
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix + (iw >> 1) - 16, iy + (ih >> 1) - 16, 3, 4));
		ifsoundonplay(sfx_hit);
		
		if(!player->invincible && !player->shyguy)
		{
			return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;
		}
	}

	return false;
}

void MO_SuperFireball::draw()
{
	spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, colorOffset + directionOffset, iw, ih);
}

//------------------------------------------------------------------------------
// class hammer
//------------------------------------------------------------------------------
MO_Hammer::MO_Hammer(gfxSprite *nspr, short x, short y, short iNumSpr, float fVelyX, float fVelyY, short aniSpeed, short iGlobalID, short teamID, short iColorID, bool superHammer) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, (short)nspr->getWidth() / iNumSpr, (short)nspr->getHeight() >> 2, 0, 0)
{
	ih = ih >> 2;

	iPlayerID = iGlobalID;
	iTeamID = teamID;
	colorOffset = iColorID * 28;
	movingObjectType = movingobject_hammer;
	
	state = 1;
	
	velx = fVelyX;
	vely = fVelyY;
	ttl = game_values.hammerttl;

	fSuper = superHammer;

	if(velx > 0.0f)
		drawframe = 0;
	else
		drawframe = animationWidth - iw;

	fObjectCollidesWithMap = false;

	sSpotlight = NULL;
}

void MO_Hammer::update()
{
	if(++animationtimer == animationspeed)
	{
		animationtimer = 0;
		
		if(velx > 0)
		{
			drawframe += iw;
			if(drawframe >= animationWidth)
				drawframe = 0;
		}
		else
		{
			drawframe -= iw;
			if(drawframe < 0)
				drawframe = animationWidth - iw;
		}
	}

	xf(fx + velx);
	yf(fy + vely);
	
	if(!fSuper)
		vely += GRAVITATION;

	if(ix < 0)
		xi(ix + 640);
	else if(ix > 639)
		xi(ix - 640);
	
	if(iy > 480 || --ttl <= 0 || (fSuper && iy < -ih))
	{
		removeifprojectile(this, false, true);
	}
	else if(game_values.spotlights)
	{
		if(!sSpotlight)
		{
			sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
		}

		if(sSpotlight)
		{
			sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
		}
	}

	//Detection collision with hammer breakable blocks
	IO_Block * blocks[4];
	GetCollisionBlocks(blocks);
	for(short iBlock = 0; iBlock < 4; iBlock++)
	{
		if(blocks[iBlock] && blocks[iBlock]->getBlockType() == block_weaponbreakable)
		{
			B_WeaponBreakableBlock * weaponbreakableblock = (B_WeaponBreakableBlock*)blocks[iBlock];
			if(weaponbreakableblock->iType == 5)
			{
				weaponbreakableblock->triggerBehavior(iPlayerID, iTeamID);
				removeifprojectile(this, false, false);
				return;
			}
		}
	}
}

bool MO_Hammer::collide(CPlayer * player)
{
	if(iPlayerID != player->globalID && (game_values.teamcollision == 2|| iTeamID != player->teamID))
	{
		if(player->shield == 0)
		{
			removeifprojectile(this, false, false);

			if(!player->invincible && !player->shyguy)
			{
				//Find the player that shot this hammer so we can attribute a kill
				PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_hammer, false, false);
				return true;
			}
		}
	}

	return false;
}

void MO_Hammer::draw()
{
	spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, colorOffset, iw, ih);
}


//------------------------------------------------------------------------------
// class sledge hammer
//------------------------------------------------------------------------------
MO_SledgeHammer::MO_SledgeHammer(gfxSprite *nspr, short x, short y, short iNumSpr, float fVelyX, float fVelyY, short aniSpeed, short iGlobalID, short iTeamID, short iColorID, bool superHammer) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, (short)nspr->getWidth() / iNumSpr, (short)nspr->getHeight() / 5, 0, 0)
{
	ih = collisionHeight;

	playerID = iGlobalID;
	teamID = iTeamID;
	colorOffset = (iColorID + 1) * 32;
	movingObjectType = movingobject_sledgehammer;
	state = 1;
	velx = fVelyX;
	vely = fVelyY;

	fSuper = superHammer;

	if(velx > 0.0f)
		drawframe = 0;
	else
		drawframe = animationWidth - iw;
}

void MO_SledgeHammer::update()
{
	if(++animationtimer == animationspeed)
	{
		animationtimer = 0;
		
		if(velx > 0)
		{
			drawframe += iw;
			if(drawframe >= animationWidth)
				drawframe = 0;
		}
		else
		{
			drawframe -= iw;
			if(drawframe < 0)
				drawframe = animationWidth - iw;
		}
	}

	xf(fx + velx);
	yf(fy + vely);
	
	vely += GRAVITATION;

	if(ix < 0)
		xi(ix + 640);
	else if(ix > 639)
		xi(ix - 640);
	
	if(iy >= 480)
		dead = true;
}

bool MO_SledgeHammer::collide(CPlayer * player)
{
	if(player->shield == 0)
	{
		dead = true;
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix + (iw >> 1) - 16, iy + (ih >> 1) - 16, 3, 4));
		ifsoundonplay(sfx_hit);
		
		if(!player->invincible && !player->shyguy)
		{
			return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;
		}
	}

	return false;
}

void MO_SledgeHammer::explode()
{
	if(fSuper)
	{
		objectcontainer[2].add(new MO_Explosion(&spr_explosion, ix + (iw >> 2) - 96, iy + (ih >> 2) - 64, 2, 4, -1, -1, kill_style_hammer));
		ifsoundonplay(sfx_bobombsound);
	}
	else
	{
		short iCenterX = ix + (iw >> 1) - 14;
		short iCenterY = iy + (ih >> 1) - 14;
		short iColorID = colorOffset / 32 - 1;

		for(short iHammer = 0; iHammer < 3; iHammer++)
		{
			float dAngle = (float)(rand() % 628) / 100.0f;
			float dVel = (float)(rand() % 5) / 2.0f + 3.0f;
			float dVelX = dVel * cos(dAngle);
			float dVelY = dVel * sin(dAngle);
			objectcontainer[2].add(new MO_Hammer(&spr_hammer, iCenterX, iCenterY, 6, dVelX, dVelY, 5, playerID, teamID, iColorID, true));
		}

		projectiles[playerID] += 3;

		ifsoundonplay(sfx_cannon);	
	}
}

void MO_SledgeHammer::draw()
{
	spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, colorOffset, iw, ih);
}


//------------------------------------------------------------------------------
// class Ice Blast
//------------------------------------------------------------------------------
MO_IceBlast::MO_IceBlast(gfxSprite *nspr, short x, short y, float fVelyX, short iGlobalID, short teamID, short iColorID) :
	IO_MovingObject(nspr, x, y, 4, 8, 32, 32, 0, 0, 0, (iColorID + 1) << 5, 32, 32)
{
	iPlayerID = iGlobalID;
	iTeamID = teamID;

	movingObjectType = movingobject_iceblast;
	
	state = 1;
	
	velx = fVelyX;
	vely = 0.0f;

	if(velx > 0.0f)
		drawframe = 0;
	else
		drawframe = animationWidth - iw;

	ttl = 120;

	fObjectCollidesWithMap = false;

	sSpotlight = NULL;
}

void MO_IceBlast::update()
{
	if(++animationtimer == animationspeed)
	{
		animationtimer = 0;
		
		if(velx > 0)
		{
			drawframe += iw;
			if(drawframe >= animationWidth)
				drawframe = 0;
		}
		else
		{
			drawframe -= iw;
			if(drawframe < 0)
				drawframe = animationWidth - iw;
		}
	}

	xf(fx + velx);
	
	if(--ttl <= 0)
	{
		removeifprojectile(this, false, true);
	}
	else if(game_values.spotlights)
	{
		if(!sSpotlight)
		{
			sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
		}

		if(sSpotlight)
		{
			sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
		}
	}
}

bool MO_IceBlast::collide(CPlayer * player)
{
	if(iPlayerID != player->globalID && (game_values.teamcollision == 2 || iTeamID != player->teamID))
	{
		if(player->shield == 0 && !player->invincible && !player->shyguy)
		{
			player->makefrozen(game_values.wandfreezetime);
			removeifprojectile(this, false, true);
		}
	}

	return false;
}

void MO_IceBlast::draw()
{
	IO_MovingObject::draw();
}

//------------------------------------------------------------------------------
// class boomerang
//------------------------------------------------------------------------------
MO_Boomerang::MO_Boomerang(gfxSprite *nspr, short x, short y, short iNumSpr, bool moveToRight, short aniSpeed, short iGlobalID, short teamID, short iColorID) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, (short)nspr->getWidth() / iNumSpr, (short)nspr->getHeight() >> 3, 0, 0)
{
	//boomerangs sprites have both right and left sprites in them
	ih = ih >> 3;

	iPlayerID = iGlobalID;
	iTeamID = teamID;
	colorOffset = iColorID * 64;
	movingObjectType = movingobject_boomerang;
	
	state = 1;
	
	fMoveToRight = moveToRight;

	if(moveToRight)
		velx = 5.0f;
	else
		velx = -5.0f;

	vely = 0.0f;

	fFlipped = false;

	//Don't let boomerang start off the screen or it won't rebound correctly
	if(moveToRight && fx + iw >= 640.0f)
		xf(fx - 640.0f);
	else if(!moveToRight && fx < 0.0f)
		xf(fx + 640.0f);

	iStateTimer = 0;

	if(game_values.boomerangstyle == 3)
		iStyle = rand() % 3;
	else
		iStyle = game_values.boomerangstyle;

	fObjectCollidesWithMap = false;

	sSpotlight = NULL;
}

void MO_Boomerang::update()
{
	if(!sfx_boomerang.isplaying())
		ifsoundonplay(sfx_boomerang);

	animate();

	//Detection collision with boomerang breakable blocks
	IO_Block * blocks[4];
	GetCollisionBlocks(blocks);
	for(short iBlock = 0; iBlock < 4; iBlock++)
	{
		if(blocks[iBlock] && blocks[iBlock]->getBlockType() == block_weaponbreakable)
		{
			B_WeaponBreakableBlock * weaponbreakableblock = (B_WeaponBreakableBlock*)blocks[iBlock];
			if(weaponbreakableblock->iType == 4)
			{
				weaponbreakableblock->triggerBehavior(iPlayerID, iTeamID);
				forcedead();
				return;
			}
		}
	}

	if(iStyle == 0) //Flat style
	{
		fOldX = fx;
		xf(fx + velx);

		if(fMoveToRight && fx + iw >= 640.0f && fOldX + iw < 640.0f)
		{
			if(fFlipped)
			{
				forcedead();
				return;
			}
			else
			{
				xf(640.0f - iw);
				fFlipped = true;
				fMoveToRight = false;
				velx = -velx;
			}
		}
		else if(!fMoveToRight && fx < 0.0f && fOldX >= 0.0f)
		{
			if(fFlipped)
			{
				forcedead();
				return;
			}
			else
			{
				xf(0.0f);
				fFlipped = true;
				fMoveToRight = true;
				velx = -velx;
			}
		}
	}
	else if(iStyle == 1)
	{
		//Attempting to emulate the SMB3 boomerang behavior
		iStateTimer++;

		fOldX = fx;
		xf(fx + velx);

		if(fx < 0.0f)
			xf(fx + 640.0f);
		else if(fx + iw >= 640.0f)
			xf(fx - 640.0f);

		if(state == 1)
		{
			fOldY = fy;
			yf(fy - 3.2f);

			if(iStateTimer >= 20)
			{
				iStateTimer = 0;
				state = 2;
			}
		}
		else if(state == 2)
		{
			if(iStateTimer >= 26)
			{
				iStateTimer = 0;
				state = 3;
			}
		}
		else if(state == 3)
		{
			fOldY = fy;
			yf(fy + 1.0f);

			if(fMoveToRight)
			{
				//Add amount so that by time fy lowers by two tiles, we turn around the boomerang
				velx -= 0.15625f;

				if(velx <= -5.0f)
				{
					velx = -5.0f;
					state = 4;
					fFlipped = true;
				}
			}
			else
			{
				velx += 0.15625f;

				if(velx >= 5.0f)
				{
					velx = 5.0f;
					state = 4;
					fFlipped = true;
				}
			}

			iStateTimer = 0;
		}
		else if(state == 4)
		{
			if(iStateTimer >= 46)
			{
				if((fMoveToRight && fx < 0.0f && fOldX >= 0.0f) ||
					(!fMoveToRight && fx + iw >= 640.0f && fOldX + iw < 640.0f))
				{
					forcedead();
					return;
				}
			}
		}
	}
	else if(iStyle == 2)   //Zelda style boomerang
	{
		iStateTimer++;

		fOldX = fx;
		xf(fx + velx);

		if(fx < 0.0f)
			xf(fx + 640.0f);
		else if(fx + iw >= 640.0f)
			xf(fx - 640.0f);

		if(iStateTimer > game_values.boomeranglife)
		{
			forcedead();
			return;
		}

		if(state == 1)
		{
			if(iStateTimer >= 64)
			{
				state = 2;
				fFlipped = true;
				
				/*
				CPlayer * player = GetPlayerFromGlobalID(iPlayerID);

				//No wrap boomerang
				if(player)
				{
					if((player->ix < ix && velx > 0) || (player->ix > ix && velx < 0))
						velx = -velx;
				}
				else
				{
					velx = -velx;
				}
				*/

				velx = -velx;  //Wrap Boomerang
			}
		}
		else if(state == 2)
		{
			fOldY = fy;
			yf(fy + vely);

			//Follow the player zelda style
			CPlayer * player = GetPlayerFromGlobalID(iPlayerID);
			
			if(player)
			{
				bool fWrap = false;
				if(abs(player->ix - ix) > 320)
					fWrap = true;

				if((player->ix < ix && !fWrap) || (player->ix > ix && fWrap))  //Wrap Boomerang
				//if(player->ix < ix)  //No Wrap Boomerang
				{
					velx -= 0.2f;
					
					if(velx < -5.0f)
						velx = -5.0f;
				}
				else 
				{
					velx += 0.2f;

					if(velx > 5.0f)
						velx = 5.0f;
				}

				if(player->iy < iy)
				{
					vely -= 0.2f;
					
					if(vely < -3.0f)
						vely = -3.0f;
				}
				else 
				{
					vely += 0.2f;

					if(vely > 3.0f)
						vely = 3.0f;
				}
			}
			else 
			{
				//Remove boomerang if player was removed from game
				forcedead();
				return;

				/*
				//Die at nearest edge if player was removed from game
				if(velx > 0)
					velx = 5.0f;
				else
					velx = -5.0f;

				if((fx < 0.0f && fOldX >= 0.0f) ||
					(fx + iw >= 640.0f && fOldX + iw < 640.0f))
				{
					forcedead();
					return;
				}
				*/
			}
		}
	}

	if(game_values.spotlights)
	{
		if(!sSpotlight)
		{
			sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
		}

		if(sSpotlight)
		{
			sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
		}
	}
}

//Call to kill boomerang when it is not caught by player
void MO_Boomerang::forcedead()
{
	removeifprojectile(this, false, true);
	ifsoundonstop(sfx_boomerang);

	if(game_values.boomeranglimit == 0)
		return;

	//Penalize player if they did not catch it
	CPlayer * player = GetPlayerFromGlobalID(iPlayerID);

	if(player)
	{
		if(player->projectilelimit > 0)
			player->DecreaseProjectileLimit();
	}
}

bool MO_Boomerang::collide(CPlayer * player)
{
	if(iPlayerID != player->globalID && (game_values.teamcollision == 2|| iTeamID != player->teamID))
	{
		if(player->shield == 0)
		{
			removeifprojectile(this, false, false);

			if(!player->invincible && !player->shyguy)
			{
				//Find the player that shot this boomerang so we can attribute a kill
				PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_boomerang, false, false);
				return true;
			}
		}
	}
	else if(iPlayerID == player->globalID && fFlipped)
	{
		removeifprojectile(this, false, true);
	}

	return false;
}

void MO_Boomerang::draw()
{
	spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, colorOffset + (fMoveToRight ? 0 : 32), iw, ih);
}

//------------------------------------------------------------------------------
// class bomb
//------------------------------------------------------------------------------
CO_Bomb::CO_Bomb(gfxSprite *nspr, short x, short y, float fVelX, float fVelY, short aniSpeed, short iGlobalID, short teamID, short iColorID, short timetolive) :
	MO_CarriedObject(nspr, x, y, 5, aniSpeed, 24, 24, 4, 13)
{
	iw = 28;
	ih = 38;

	iPlayerID = iGlobalID;
	iTeamID = teamID;
	iColorOffsetY = (iColorID + 1) * 38;
	
	movingObjectType = movingobject_bomb;
	state = 1;

	ttl = timetolive;

	velx = fVelX;
	vely = fVelY;

	iOwnerRightOffset = 14;
	iOwnerLeftOffset = -16;
	iOwnerUpOffset = 40;

	sSpotlight = NULL;
}

bool CO_Bomb::collide(CPlayer * player)
{
	if(state == 1 && owner == NULL)
	{
		if(player->AcceptItem(this))
		{
			owner = player;

			velx = 0.0f;
			vely = 0.0f;

			if(iPlayerID == -1)
			{
				iPlayerID = owner->globalID;
				iTeamID = owner->teamID;
				iColorOffsetY = (owner->colorID + 1) * 38;
			}
		}
	}
	
	return false;
}

void CO_Bomb::update()
{
	if(--ttl <= 0)
		Die();

	if(dead)
		return;

	if(owner)
	{
		MoveToOwner();
	}
	else
	{
		applyfriction();

		//Collision detect map
		fOldX = fx;
		fOldY = fy;

		collision_detection_map();
	}

	animate();

	if(game_values.spotlights)
	{
		if(!sSpotlight)
		{
			sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
		}

		if(sSpotlight)
		{
			sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
		}
	}
}

void CO_Bomb::draw()
{
	if(owner && owner->iswarping())
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iColorOffsetY, iw, ih, (short)owner->state % 4, owner->GetWarpPlane());
	else
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iColorOffsetY, iw, ih);
}

void CO_Bomb::Die()
{
	if(owner)
	{
		owner->carriedItem = NULL;
		owner = NULL;
	}

	if(iPlayerID > -1 && projectiles[iPlayerID] > 0)
		projectiles[iPlayerID]--;

	dead = true;
	objectcontainer[2].add(new MO_Explosion(&spr_explosion, ix + (iw >> 2) - 96, iy + (ih >> 2) - 64, 2, 4, iPlayerID, iTeamID, kill_style_bomb));
	ifsoundonplay(sfx_bobombsound);
}

//------------------------------------------------------------------------------
// class coin (for coin mode)
//------------------------------------------------------------------------------
MO_Coin::MO_Coin(gfxSprite *nspr, float dvelx, float dvely, short ix, short iy, short color, short team, short type, short uncollectabletime, bool placecoin) :
	IO_MovingObject(nspr, ix, iy, 4, 8, 30, 30, 1, 1, 0, color << 5, 32, 32)
{
	state = 1;
	objectType = object_moving;
	movingObjectType = movingobject_coin;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;

	iType = type;
	iTeam = team;

	iUncollectableTime = uncollectabletime;
	velx = dvelx;
	vely = dvely;

	timer = 0;
	if(placecoin)
	{
		placeCoin();
	}

	if(iType == 0)
	{
		fObjectCollidesWithMap = false;
	}
	else
	{
		collision_detection_checksides();
	}
}

bool MO_Coin::collide(CPlayer * player)
{
	if(iUncollectableTime > 0 || (iType == 1 && (!game_values.gamemodesettings.greed.owncoins && iTeam == player->getTeamID())))
		return false;

	if(!game_values.gamemode->gameover)
	{
		if(iType == 2)
			player->score->subscore[0]++;
		else
			player->score->AdjustScore(1);

		game_values.gamemode->CheckWinner(player);
	}

	eyecandy[2].add(new EC_SingleAnimation(&spr_coinsparkle, ix, iy, 7, 4));

	ifsoundonplay(sfx_coin);
	
	if(iType == 0)
		placeCoin();
	else
		dead = true;

	return false;
}

void MO_Coin::update()
{
	if(iType != 1)
	{
		animate();

		if(++sparkleanimationtimer >= 4)
		{
			sparkleanimationtimer = 0;
			sparkledrawframe += 32;
			if(sparkledrawframe >= 480)
				sparkledrawframe = 0;
		}

		if(++timer > 1000)
			placeCoin();
	}

	if(iType != 0)
	{
		applyfriction();
		IO_MovingObject::update();

		iUncollectableTime--;

		if(iType == 1 && iUncollectableTime < -game_values.gamemodesettings.greed.coinlife)
		{
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix, iy, 3, 8));
			dead = true;
		}
	}
}

void MO_Coin::draw()
{
	IO_MovingObject::draw();

	//Draw sparkles
	if(iType != 1)
		spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
}

void MO_Coin::placeCoin()
{
	timer = 0;
	
	short x = 0, y = 0;
	short iAttempts = 32;
	while((!g_map.findspawnpoint(5, &x, &y, collisionWidth, collisionHeight, false) ||
		objectcontainer[1].getClosestMovingObject(x, y, movingobject_coin) < 150.0f)
		&& iAttempts-- > 0);

	xi(x);
	yi(y);
}


//------------------------------------------------------------------------------
// class thwomp (for thwomp mode)
//------------------------------------------------------------------------------
OMO_Thwomp::OMO_Thwomp(gfxSprite *nspr, short x, float nspeed) :
	IO_OverMapObject(nspr, x, (short)-nspr->getHeight(), 1, 0)
{
	objectType = object_thwomp;
	vely = nspeed;
}

void OMO_Thwomp::update()
{
	IO_OverMapObject::update();

	if(iy > 479)
		dead = true;
}

bool OMO_Thwomp::collide(CPlayer * player)
{
	if(!player->invincible && player->shield == 0 && (player->score->score > 0 || game_values.gamemode->goal == -1))
		return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;

	return false;
}


//------------------------------------------------------------------------------
// class podobo (for survival mode)
//------------------------------------------------------------------------------
MO_Podobo::MO_Podobo(gfxSprite *nspr, short x, short y, float dVelY, short playerid, short teamid, short colorid, bool isSpawned) :
	IO_MovingObject(nspr, x, y, 4, 6)
{
	fIsSpawned = isSpawned;
	iHiddenPlane = y;

	objectType = object_moving;
	movingObjectType = movingobject_podobo;
	vely = dVelY;

	ih = 32;
	collisionHeight = ih;

	iPlayerID = playerid;
	iTeamID = teamid;
	iColorOffsetY = (colorid + 1) * 64;

	fObjectCollidesWithMap = false;
}

void MO_Podobo::update()
{
	//Special slow podobo gravity
	vely += 0.2f;

	xf(fx + velx);
	yf(fy + vely);

	animate();

	if(iy > 479 && vely > 0.0f)
		dead = true;
}

void MO_Podobo::draw()
{
	if(fIsSpawned && vely < 0.0f)
		spr->draw(ix, iy, drawframe, iColorOffsetY + (vely > 0.0f ? 32 : 0), iw, ih, 2, iHiddenPlane);
	else
		spr->draw(ix, iy, drawframe, iColorOffsetY + (vely > 0.0f ? 32 : 0), iw, ih);
}

bool MO_Podobo::collide(CPlayer * player)
{
	if(player->globalID != iPlayerID && (game_values.teamcollision == 2|| iTeamID != player->teamID) && !player->invincible && player->shield == 0 && !player->shyguy)
	{
		//Find the player that made this explosion so we can attribute a kill
		PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_podobo, false, false);
		return true;
	}

	return false;
}

void MO_Podobo::collide(IO_MovingObject * object)
{
	if(iPlayerID == -1)
		return;

	MovingObjectType type = object->getMovingObjectType();

	if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_bulletbill)
	{
		//Same team bullet bills don't kill each other
		if(type == movingobject_bulletbill && ((MO_BulletBill*) object)->iTeamID == iTeamID)
			return;

		if(type == movingobject_shell)
			((CO_Shell*)object)->Die();
		else if(type == movingobject_throwblock)
			((CO_ThrowBlock*) object)->Die();
		else if(type == movingobject_bulletbill)
			((MO_BulletBill*) object)->Die();

		ifsoundonplay(sfx_kicksound);
	}
}

//------------------------------------------------------------------------------
// class Bowser Fire (for survival mode)
//------------------------------------------------------------------------------
OMO_BowserFire::OMO_BowserFire(gfxSprite *nspr, short x, short y, float dVelX, float dVelY, short id, short teamid, short colorid) :
	IO_OverMapObject(nspr, x, y, 3, 6)
{
	objectType = object_bowserfire;
	velx = dVelX;
	vely = dVelY;

	ih = 32;
	collisionHeight = ih;

	iPlayerID = id;
	iTeamID = teamid;
	iColorOffsetY = (colorid + 1) * 64;
}

void OMO_BowserFire::update()
{
	IO_OverMapObject::update();

	if((velx < 0 && ix < -iw) || (velx > 0 && ix > 640))
	{
		if(iPlayerID != -1 && projectiles[iPlayerID] > 0)
			projectiles[iPlayerID]--;

		dead = true;
	}
}

void OMO_BowserFire::draw()
{
	spr->draw(ix, iy, drawframe, (velx > 0.0f ? 32 : 0) + iColorOffsetY, iw, ih);
}

bool OMO_BowserFire::collide(CPlayer * player)
{
	//if the fire is off the screen, don't wrap it to collide
	if((ix < 0 && velx < 0.0f && player->ix > ix + iw && player->ix + PW < 640) ||
		(ix + iw >= 640 && velx > 0.0f && player->ix + PW < ix && player->ix >= 0))
	{
		return false;
	}

	if(player->globalID != iPlayerID && (game_values.teamcollision == 2|| iTeamID != player->teamID) && !player->invincible && player->shield == 0)
	{
		//Find the player that made this explosion so we can attribute a kill
		PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_fireball, false, false);
		return true;
	}

	return false;
}


//------------------------------------------------------------------------------
// class CarriedObject - all objects players can carry inheirit from this class
//------------------------------------------------------------------------------

MO_CarriedObject::MO_CarriedObject(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY)
{
	init();
}

MO_CarriedObject::MO_CarriedObject(gfxSprite *nspr, short x, short y, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY, short iAnimationOffsetX, short iAnimationOffsetY, short iAnimationHeight, short iAnimationWidth) :
	IO_MovingObject(nspr, x, y, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY, iAnimationOffsetX, iAnimationOffsetY, iAnimationHeight, iAnimationWidth)
{
	init();
}

MO_CarriedObject::~MO_CarriedObject()
{}

void MO_CarriedObject::init()
{
	owner = NULL;
	fSmoking = false;
	
	dKickX = 2.0f;
	dKickY = 4.0f;

	iOwnerRightOffset = HALFPW;
	iOwnerLeftOffset = HALFPW - 32;
	iOwnerUpOffset = 32;

	fCarriedByKuriboShoe = false;
}

void MO_CarriedObject::draw()
{
	if(owner && owner->iswarping())
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY, iw, ih, (short)owner->state % 4, owner->GetWarpPlane());
	else
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY, iw, ih);
}

void MO_CarriedObject::MoveToOwner()
{
	if(owner)
	{
		xi(owner->ix + (owner->IsPlayerFacingRight() ? iOwnerRightOffset : iOwnerLeftOffset));
		yi(owner->iy + PH - iOwnerUpOffset + collisionOffsetY);
	}
}

void MO_CarriedObject::Drop()
{
	if(owner)
		owner->carriedItem = NULL;

	owner = NULL;

	collision_detection_checksides();
}

void MO_CarriedObject::Kick()
{
	if(owner)
	{
		velx = owner->velx + (owner->IsPlayerFacingRight() ? dKickX : -dKickX);
		vely = -dKickY;
		ifsoundonplay(sfx_kicksound);
	}

	Drop();
}

//------------------------------------------------------------------------------
// class egg (for egg mode)
//------------------------------------------------------------------------------
CO_Egg::CO_Egg(gfxSprite *nspr, short iColor) :
	MO_CarriedObject(nspr, 0, 0, 2, 16, 28, 30, 2, 1, 0, iColor << 5, 32, 32)
{
	state = 1;
	movingObjectType = movingobject_egg;

	owner_throw = NULL;
	owner_throw_timer = 0;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;
	
	color = iColor;

	egganimationrates[0] = 2;
	egganimationrates[1] = 4;
	egganimationrates[2] = 6;
	egganimationrates[3] = 8;
	egganimationrates[4] = 12;
	egganimationrates[5] = 16;

	iOwnerRightOffset = HALFPW;
	iOwnerLeftOffset = HALFPW - 28;
	iOwnerUpOffset = 32;

	fCarriedByKuriboShoe = true;

	placeEgg();
}

bool CO_Egg::collide(CPlayer * player)
{
	if(owner == NULL && player->isready())
	{
		if(player->AcceptItem(this))
		{
			owner = player;
			owner_throw = player;
		}
	}

	return false;
}

void CO_Egg::update()
{
	if(owner)
	{
		MoveToOwner();
		relocatetimer = 0;
		owner_throw = owner;
	}
	else if(++relocatetimer > 1500)
	{
		placeEgg();
		owner_throw = NULL;
	}
	else
	{
		if(owner_throw && --owner_throw_timer <= 0)
		{
			owner_throw_timer = 0;
			owner_throw = NULL;
		}

		applyfriction();

		//Collision detect map
		fOldX = fx;
		fOldY = fy;

		collision_detection_map();
	}

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}

	//Explode
	if(game_values.gamemodesettings.egg.explode > 0)
	{
		if(--explosiondrawtimer <= 0)
		{
			explosiondrawtimer = 62;
			if(--explosiondrawframe < 0)
			{
				objectcontainer[2].add(new MO_Explosion(&spr_explosion, ix + (iw >> 1) - 96, iy + (ih >> 1) - 64, 2, 4, -1, -1, kill_style_bomb));
				placeEgg();

				ifsoundonplay(sfx_bobombsound);
			}
			else
			{
				if(explosiondrawframe < 5)
					animationspeed = egganimationrates[explosiondrawframe];
				else
					animationspeed = egganimationrates[5];
			}
		}
	}
	
	animate();
}

void CO_Egg::draw()
{
	MO_CarriedObject::draw();

	//Display explosion timer
	if(game_values.gamemodesettings.egg.explode > 0 && explosiondrawframe < 5)
	{
		if(owner && owner->iswarping())
			spr_eggnumbers.draw(ix - collisionOffsetX, iy - collisionOffsetY, explosiondrawframe << 5, color << 5, 32, 32, (short)owner->state % 4, owner->GetWarpPlane());
		else
			spr_eggnumbers.draw(ix - collisionOffsetX, iy - collisionOffsetY, explosiondrawframe << 5, color << 5, 32, 32);
	}
}

void CO_Egg::placeEgg()
{
	relocatetimer = 0;
	if(game_values.gamemodesettings.egg.explode > 0)
	{
		explosiondrawframe = game_values.gamemodesettings.egg.explode - 1;
		explosiondrawtimer = 62;

		if(explosiondrawframe < 5)
			animationspeed = egganimationrates[explosiondrawframe];
		else
			animationspeed = egganimationrates[5];
	}
	else
	{
		animationspeed = egganimationrates[5];
	}
	
	short x = 0, y = 0;
	short iAttempts = 32;
	while((!g_map.findspawnpoint(5, &x, &y, collisionWidth, collisionHeight, false) ||
		objectcontainer[1].getClosestMovingObject(x, y, movingobject_yoshi) < 250.0f)
		&& iAttempts-- > 0);

	xi(x);
	yi(y);

	vely = GRAVITATION;
	velx = 0.0f;

	owner_throw = NULL;
	owner_throw_timer = 0;

	Drop();
}

void CO_Egg::Drop()
{
	MO_CarriedObject::Drop();
	owner_throw_timer = 62;
}

//------------------------------------------------------------------------------
// class star (for star mode)
//------------------------------------------------------------------------------
CO_Star::CO_Star(gfxSprite *nspr, short type, short id) :
	MO_CarriedObject(nspr, 0, 0, 8, 8, 30, 30, 1, 1)
{
	iID = id;

	state = 1;
	iw = 32;
	ih = 32;
	movingObjectType = movingobject_star;
	
	iType = type;

	iOffsetY = type == 1 ? 32 : 0;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;

	dKickX = 3.0f;
	dKickY = 6.0f;

	fCarriedByKuriboShoe = true;

	placeStar();

	iOwnerRightOffset = 14;
	iOwnerLeftOffset = -22;
	iOwnerUpOffset = 32;
}

bool CO_Star::collide(CPlayer * player)
{
	if(game_values.gamemode->gamemode != game_mode_star)
		return false;

	CGM_Star * starmode = (CGM_Star *)game_values.gamemode;

	timer = 0;
	if(owner == NULL && player->isready())
	{
		if(player->throw_star == 0 && player->AcceptItem(this))
		{
			owner = player;
		}
	}

	if((iType == 0 && player->invincible) || player->shield > 0 || starmode->isplayerstar(player) || game_values.gamemode->gameover)
		return false;

	CPlayer * oldstar = starmode->swapplayer(iID, player);

	if(owner == oldstar)
	{
		oldstar->throw_star = 30;
		Kick();
	}

	return false;
}

void CO_Star::update()
{
	if(owner)
	{
		MoveToOwner();
		timer = 0;
	}
	else if(++timer > 300)
	{
		placeStar();
	}
	else
	{
		applyfriction();
		
		//Collision detect map
		IO_MovingObject::update();
	}

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}
}

void CO_Star::draw()
{
	if(owner)
	{
		if(owner->iswarping())
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, iOffsetY, iw, ih, (short)owner->state % 4, owner->GetWarpPlane());
		else
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, iOffsetY, iw, ih);
	}
	else
	{
		if(velx != 0.0f)
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, iOffsetY, iw, ih);  //keep the star still while it's moving
		else
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iOffsetY, iw, ih);
	}

	if(owner && owner->iswarping())
		spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, iType ? 0 : 32, 32, 32, (short)owner->state % 4, owner->GetWarpPlane());
	else
		spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, iType ? 0 : 32, 32, 32);
}

void CO_Star::placeStar()
{
	if(game_values.gamemode->gamemode != game_mode_star)
		return;

	CGM_Star * starmode = (CGM_Star*)game_values.gamemode;

	timer = 0;

	CPlayer * star = starmode->getstarplayer(iID);

	if(star)
	{
		xf(star->fx + HALFPW - 16.0f);
		yf(star->fy + HALFPH - 17.0f);

		velx = star->velx;
		vely = star->vely;
	}

	Drop();
}


//------------------------------------------------------------------------------
// class flag base (for CTF mode)
//------------------------------------------------------------------------------
MO_FlagBase::MO_FlagBase(gfxSprite *nspr, short iTeamID, short iColorID) :
	IO_MovingObject(nspr, 1280, 960, 5, 0)  //use 1280 and 960 so when placing base, it doesn't interfere (look in getClosestObject())
{
	state = 1;
	iw = 32;
	ih = 32;
	collisionWidth = 32;
	collisionHeight = 32;

	objectType = object_moving;
	movingObjectType = movingobject_flagbase;
	teamID = iTeamID;
	iGraphicOffsetX = iColorID * 48;
	
	angle = (float)(rand()%1000 * 0.00628f);
	anglechange = (float)(rand()%100 * 0.0002f);
	anglechangetimer = (short)(rand() % 50 + 100);

	velx = sin(angle);
	vely = cos(angle);

	homeflag = NULL;

	placeFlagBase(true);

	speed = (float)game_values.gamemodesettings.flag.speed / 4.0f;

	timer = 0;

	fObjectCollidesWithMap = false;
}

bool MO_FlagBase::collide(CPlayer * player)
{
	if(teamID == player->teamID && player->carriedItem && player->carriedItem->getMovingObjectType() == movingobject_flag)
	{
		CO_Flag * flag = (CO_Flag*)player->carriedItem;
		scoreFlag(flag, player);
		timer = 0;
	}

	return false;
}

void MO_FlagBase::draw()
{
	spr->draw(ix - 8, iy - 8, iGraphicOffsetX, 0, 48, 48);
}


void MO_FlagBase::update()
{
	if(game_values.gamemodesettings.flag.speed > 0)
	{
		if(--anglechangetimer <= 0)
		{
			anglechange = (float)((rand()%101 - 50) * 0.0002f);
			anglechangetimer = (short)(rand() % 50 + 100);
		}
		
		angle += anglechange;

		velx = speed * sin(angle);
		vely = speed * cos(angle);
		
		xf(fx + velx);
		yf(fy + vely);

		if(ix < 0)
		{
			velx = -velx;
			ix = 0;
			fx = (float)ix;

			angle = atan2(velx, vely);
		}
		else if(ix + collisionWidth >= 640)
		{
			velx = -velx;
			ix = 639 - collisionWidth;
			fx = (float)ix;

			angle = atan2(velx, vely);
		}
		
		if(iy < 0)
		{
			vely = -vely;
			iy = 0;
			fy = (float)iy;

			angle = atan2(velx, vely);
		}
		else if(iy + collisionHeight >= 480)
		{
			vely = -vely;
			iy = 479 - collisionHeight;
			fy = (float)iy;

			angle = atan2(velx, vely);
		}
	}

	if(game_values.gamemodesettings.flag.speed == 0 && timer++ > 1000)
		placeFlagBase(false);
}

void MO_FlagBase::placeFlagBase(bool fInit)
{
	timer = 0;
	short x = 0, y = 0;

	if(fInit && teamID < g_map.iNumFlagBases)
	{
		x = g_map.flagbaselocations[teamID].x;
		y = g_map.flagbaselocations[teamID].y;
	}
	else
	{
		short iAttempts = 32;
		while((!g_map.findspawnpoint(5, &x, &y, collisionWidth, collisionHeight, false) ||
			objectcontainer[1].getClosestMovingObject(x, y, movingobject_flagbase) < 200.0f)
			&& iAttempts-- > 0);
	}

	xi(x);
	yi(y);
}

void MO_FlagBase::collide(IO_MovingObject * object)
{
	if(object->getMovingObjectType() == movingobject_flag)
	{
		CO_Flag * flag = (CO_Flag*)object;
		CPlayer * player = flag->owner_throw;

		if(player)
		{
			if(teamID == player->teamID)
			{
				scoreFlag(flag, player);
			}
		}
	}
}

void MO_FlagBase::scoreFlag(CO_Flag * flag, CPlayer * player)
{
	if(flag->teamID == teamID)
	{
		flag->placeFlag();
		ifsoundonplay(sfx_areatag);
	}
	else if(!game_values.gamemodesettings.flag.homescore || homeflag != NULL || game_values.gamemodesettings.flag.centerflag)
	{
		flag->placeFlag();
		if(!game_values.gamemode->gameover)
		{
			player->score->AdjustScore(1);
			game_values.gamemode->CheckWinner(player);
		}
		
		ifsoundonplay(sfx_racesound);

		if(game_values.gamemodesettings.flag.pointmove)
		{
			//Set the values way outside the map so it will place the base correctly
			ix = 1280;
			iy = 960;
			placeFlagBase(false);
		}
	}
}

//------------------------------------------------------------------------------
// class flag (for Capture the Flag mode)
//------------------------------------------------------------------------------
CO_Flag::CO_Flag(gfxSprite *nspr, MO_FlagBase * base, short iTeamID, short iColorID) :
	MO_CarriedObject(nspr, 0, 0, 4, 8, 30, 30, 1, 1, 0, iColorID << 6, 32, 32)
{
	state = 1;
	movingObjectType = movingobject_flag;
	flagbase = base;
	teamID = iTeamID;
	fLastFlagDirection = false;
	owner_throw = NULL;
	owner_throw_timer = 0;

	centerflag = teamID == -1;

	iOwnerRightOffset = HALFPW - 31;
	iOwnerLeftOffset = HALFPW + 1;
	iOwnerUpOffset = 38;

	fCarriedByKuriboShoe = true;

	placeFlag();
}

bool CO_Flag::collide(CPlayer * player)
{
	if(owner == NULL && player->isready() && (!fInBase || teamID != player->teamID))
	{
		if(game_values.gamemodesettings.flag.touchreturn && teamID == player->teamID)
		{
			placeFlag();
			ifsoundonplay(sfx_areatag);
		}
		else if(player->AcceptItem(this))
		{
			owner = player;
			owner_throw = player;
			
			if(flagbase)
				flagbase->setFlag(NULL);
		}
	}
	
	return false;
}

void CO_Flag::update()
{
	if(owner)
	{
		MoveToOwner();
		timer = 0;
		fInBase = false;
		owner_throw = owner;
	}
	else if(fInBase)
	{
		if(flagbase)
		{
			xf(flagbase->fx);
			yf(flagbase->fy);
		}

		owner_throw = NULL;
	}
	else if(game_values.gamemodesettings.flag.autoreturn > 0 && ++timer > game_values.gamemodesettings.flag.autoreturn)
	{
		timer = 0;
		placeFlag();
		owner_throw = NULL;
	}
	else
	{
		if(owner_throw && --owner_throw_timer < 0)
		{
			owner_throw_timer = 0;
			owner_throw = NULL;
		}

		applyfriction();

		//Collision detect map
		fOldX = fx;
		fOldY = fy;

		collision_detection_map();
	}

	animate();

	if(centerflag)
	{
		if(animationtimer % 2 == 0)
		{
			animationOffsetY += 64;
			if(animationOffsetY > 192)
				animationOffsetY = 0;
		}
	}
}

void CO_Flag::draw()
{
	if(owner)
	{
		if(centerflag)
			animationOffsetY = owner->colorID << 6;

		if(owner->iswarping())
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY + (fLastFlagDirection ? 32 : 0), iw, ih, (short)owner->state % 4, owner->GetWarpPlane());
		else
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY + (fLastFlagDirection ? 32 : 0), iw, ih);
	}
	else
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY + (fLastFlagDirection ? 32 : 0), iw, ih);
	}
}

void CO_Flag::MoveToOwner()
{
	MO_CarriedObject::MoveToOwner();

	if(owner)
		fLastFlagDirection = owner->IsPlayerFacingRight();
}

void CO_Flag::placeFlag()
{
	if(centerflag)
	{
		Drop();
		fInBase = false;
	
		short iAttempts = 10;
		while(!g_map.findspawnpoint(5, &ix, &iy, collisionWidth, collisionHeight, false) && iAttempts-- > 0);
		fx = (float)ix;
		fy = (float)iy;

		velx = 0.0f;
		vely = 0.0f;
		fLastFlagDirection = false;
	}
	else if(flagbase)
	{
		Drop();
		fInBase = true;
		xi(flagbase->ix);
		yi(flagbase->iy);
		fLastFlagDirection = false;
		flagbase->setFlag(this);
	}
	
	owner_throw = NULL;
	owner_throw_timer = 0;

	timer = 0;
}

void CO_Flag::Drop()
{
	MO_CarriedObject::Drop();
	owner_throw_timer = 62;
}

//------------------------------------------------------------------------------
// class yoshi (for egg mode)
//------------------------------------------------------------------------------
MO_Yoshi::MO_Yoshi(gfxSprite *nspr, short iColor) :
	IO_MovingObject(nspr, 0, 0, 2, 8, 52, 56, 0, 0, 0, iColor * 56, 56, 52)
{
	objectType = object_moving;
	movingObjectType = movingobject_yoshi;
	state = 1;

	color = iColor;

	placeYoshi();

	fObjectCollidesWithMap = false;
}

bool MO_Yoshi::collide(CPlayer * player)
{
	if(player->carriedItem && player->carriedItem->getMovingObjectType() == movingobject_egg)
	{
		CO_Egg * egg = (CO_Egg*)player->carriedItem;

		if(egg->color == color)
		{
			if(!game_values.gamemode->gameover)
			{
				player->score->AdjustScore(1);
				game_values.gamemode->CheckWinner(player);
			}
		
			placeYoshi();

			CO_Egg * egg = (CO_Egg*)player->carriedItem;
			egg->placeEgg();

			ifsoundonplay(sfx_yoshi);
		}
	}

	return false;
}

void MO_Yoshi::update()
{
	animate();

	if(++timer > 2000)
		placeYoshi();
}

void MO_Yoshi::placeYoshi()
{
	timer = 0;

	for(short tries = 0; tries < 64; tries++)
	{
		ix = (short)(rand()%(640 - iw));
		iy = (short)(rand()%(480 - ih - TILESIZE));	//don't spawn too low

		short ixl = ix / TILESIZE;
		short ixr = (ix + iw) / TILESIZE;
		short iyt = iy / TILESIZE;
		short iyb = (iy + ih) / TILESIZE;

		int upperLeft = g_map.map(ixl, iyt);
		int upperRight = g_map.map(ixr, iyt);
		int lowerLeft = g_map.map(ixl, iyb);
		int lowerRight = g_map.map(ixr, iyb);

		if((upperLeft & tile_flag_solid) == 0 && (upperRight & tile_flag_solid) == 0 &&
			(lowerLeft & tile_flag_solid) == 0 && (lowerRight & tile_flag_solid) == 0 && 
			!g_map.block(ixl, iyt) && !g_map.block(ixr, iyt) && !g_map.block(ixl, iyb) && !g_map.block(ixr, iyb))
		{
			//spawn on ground, but not on spikes
			short iDeathY = (iy+ih)/TILESIZE;
			short iDeathX1 = ix/TILESIZE;
			short iDeathX2 = (ix+iw)/TILESIZE;
			
			while(iDeathY < MAPHEIGHT)
			{
				int ttLeftTile = g_map.map(iDeathX1, iDeathY);
				int ttRightTile = g_map.map(iDeathX2, iDeathY);

				if(((ttLeftTile & tile_flag_solid || ttLeftTile & tile_flag_solid_on_top) && (ttLeftTile & tile_flag_death_on_top) == 0) ||  
					((ttRightTile & tile_flag_solid || ttRightTile & tile_flag_solid_on_top) && (ttRightTile & tile_flag_death_on_top) == 0) ||
					g_map.block(iDeathX1, iDeathY) || g_map.block(iDeathX2, iDeathY))
				{
					short top = ((iDeathY << 5) - ih) / TILESIZE;
					
					if(g_map.spawn(1, iDeathX1, top) && g_map.spawn(1, iDeathX2, top) && 
						g_map.spawn(1, iDeathX1, iDeathY - 1) && g_map.spawn(1, iDeathX2, iDeathY - 1))
					{
						xi(ix);
						yi((iDeathY << 5) - ih);
						return;
					}

					break;
				}
				else if(ttLeftTile & tile_flag_death_on_top || ttRightTile & tile_flag_death_on_top)
				{
					break;
				}

				iDeathY++;
			}
		}
	}

	ix = 320;
	iy = 240;
}

void MO_Yoshi::collide(IO_MovingObject * object)
{
	if(object->getMovingObjectType() == movingobject_egg)
	{
		CO_Egg * egg = (CO_Egg*)object;

		if(egg->color == color && egg->owner_throw)
		{
			CPlayer * player = egg->owner_throw;

			if(!game_values.gamemode->gameover)
			{
				player->score->AdjustScore(1);
				game_values.gamemode->CheckWinner(player);
			}

			placeYoshi();
			egg->placeEgg();

			ifsoundonplay(sfx_yoshi);
		}
	}
}

//------------------------------------------------------------------------------
// class area (for Domination mode)
//------------------------------------------------------------------------------
OMO_Area::OMO_Area(gfxSprite *nspr, short iNumAreas) :
	IO_OverMapObject(nspr, 1280, 960, 5, 0)
{
	iw = (short)spr->getWidth() / 5;
	collisionWidth = iw;

	objectType = object_area;
	iPlayerID = -1;
	iTeamID = -1;

	colorID = -1;
	scoretimer = 0;
	
	state = 1;
	
	frame = 0;
	relocatetimer = 0;
	numareas = iNumAreas;
	totalTouchingPlayers = 0;
	touchingPlayer = NULL;
	
	placeArea();
}

bool OMO_Area::collide(CPlayer * player)
{
	if(player->statue_timer == 0 && !player->isdead())
	{
		totalTouchingPlayers++;

		if(totalTouchingPlayers == 1)
		{
			touchingPlayer = player;
		}
		else
		{
			touchingPlayer = NULL;
			reset();
		}
	}

	return false;
}

void OMO_Area::draw()
{
	spr->draw(ix, iy, frame, 0, iw, ih);
}

void OMO_Area::update()
{
	if(touchingPlayer)
		setOwner(touchingPlayer);

	if(iPlayerID != -1 && !game_values.gamemode->gameover)
	{
		if(++scoretimer >= (game_values.pointspeed << 1))
		{
			scoretimer = 0;
			list_players[iPlayerID]->score->AdjustScore(1);
			game_values.gamemode->CheckWinner(list_players[iPlayerID]);
		}
	}

	if(game_values.gamemodesettings.domination.relocationfrequency > 0)
	{
		if(++relocatetimer >= game_values.gamemodesettings.domination.relocationfrequency)
		{
			relocatetimer = 0;
			placeArea();
		}
	}

	totalTouchingPlayers = 0;
	touchingPlayer = NULL;
}

void OMO_Area::placeArea()
{
	short x = 0, y = 0;
	short iAttempts = 32;
	while((!g_map.findspawnpoint(5, &x, &y, collisionWidth, collisionHeight, false) ||
		objectcontainer[0].getClosestObject(x, y, object_area) <= (200.0f - ((numareas - 3) * 25.0f)))
		&& iAttempts-- > 0);

	xi(x);
	yi(y);
}

void OMO_Area::reset()
{
	iPlayerID = -1;
	iTeamID = -1;

	colorID = -1;
	scoretimer = 0;
	frame = 0;
}

void OMO_Area::setOwner(CPlayer * player)
{
	if(colorID != player->colorID)
	{
		iPlayerID = player->localID;
		iTeamID = player->teamID;
		colorID = player->colorID;

		frame = (colorID + 1) * iw;
		ifsoundonplay(sfx_areatag);
	}
}

//------------------------------------------------------------------------------
// class KingOfTheHillArea (for King of the Hill mode)
//------------------------------------------------------------------------------
OMO_KingOfTheHillZone::OMO_KingOfTheHillZone(gfxSprite *nspr) :
	IO_OverMapObject(nspr, 0, 0, 5, 0)
{
	size = game_values.gamemodesettings.kingofthehill.areasize;

	if(size < 2)
		game_values.gamemodesettings.kingofthehill.areasize = size = 2;

	iw = TILESIZE * size;
	collisionWidth = iw;
	ih = TILESIZE * size;
	collisionHeight = ih;

	objectType = object_kingofthehill_area;
	colorID = -1;
	scoretimer = 0;
	
	state = 1;
	
	frame = 0;
	relocatetimer = 0;
	iPlayerID = -1;
	
	for(short iPlayer = 0; iPlayer < 4; iPlayer++)
	{
		playersTouching[iPlayer] = NULL;
		playersTouchingCount[iPlayer] = 0;
	}

	totalTouchingPlayers = 0;
	
	multiplier = 1;
	multipliertimer = 0;

	placeArea();
}

bool OMO_KingOfTheHillZone::collide(CPlayer * player)
{
	if(player->statue_timer == 0)
	{
		playersTouching[player->teamID] = player;
		playersTouchingCount[player->teamID]++;
		totalTouchingPlayers++;
	}
	return false;
}

void OMO_KingOfTheHillZone::draw()
{
	for(short iRow = 0; iRow < size; iRow++)
	{
		short iYPiece = TILESIZE;
		if(iRow == 0)
			iYPiece = 0;
		if(iRow == size - 1)
			iYPiece = TILESIZE * 2;

		for(short iCol = 0; iCol < size; iCol++)
		{
			short iXPiece = TILESIZE;
			if(iCol == 0)
				iXPiece = 0;
			if(iCol == size - 1)
				iXPiece = TILESIZE * 2;

			short iColX = ix + (iCol << 5);
			short iRowX = iy + (iRow << 5);
						
			if(multiplier > 1)
				spr_awardkillsinrow.draw(iColX + 8, iRowX + 8, (multiplier - 1) << 4, colorID << 4, 16, 16);

			spr->draw(iColX, iRowX, iXPiece + frame, iYPiece, TILESIZE, TILESIZE);
		}
	}	
}

void OMO_KingOfTheHillZone::update()
{
	short iMax = 0; 
	short iMaxTeam = -1;

	for(short iTeam = 0; iTeam < 4; iTeam++)
	{
		if(playersTouchingCount[iTeam] > iMax)
		{
			iMax = playersTouchingCount[iTeam];
			iMaxTeam = iTeam;
		}
	}

	if((iMax << 1) > totalTouchingPlayers) //If the max touching player team is greater than the rest of the touching players
	{
		colorID = playersTouching[iMaxTeam]->colorID;
		iPlayerID = playersTouching[iMaxTeam]->localID;
		frame = ((colorID + 1) << 5) * 3;
	}
	else
	{
		colorID = -1;
		iPlayerID = -1;
		frame = 0;
	}

	if(iPlayerID != -1 && !game_values.gamemode->gameover)
	{
		scoretimer += (iMax << 1) - totalTouchingPlayers;  //Speed of point accumulation is proportional to how many players are in zone

		if(scoretimer >= game_values.pointspeed)
		{
			scoretimer = 0;
			list_players[iPlayerID]->score->AdjustScore(multiplier);
			game_values.gamemode->CheckWinner(list_players[iPlayerID]);

			if(game_values.gamemodesettings.kingofthehill.maxmultiplier > 1 && ++multipliertimer >= 5)
			{
				multipliertimer = 0;

				if(multiplier < game_values.gamemodesettings.kingofthehill.maxmultiplier)
					multiplier++;
			}
		}
	}
	else
	{
		multiplier = 1;
		multipliertimer = 0;
	}

	if(game_values.gamemodesettings.kingofthehill.relocationfrequency > 0)
	{
		if(++relocatetimer >= game_values.gamemodesettings.kingofthehill.relocationfrequency)
		{
			relocatetimer = 0;
			placeArea();
		}
	}

	for(short iPlayer = 0; iPlayer < 4; iPlayer++)
	{
		playersTouching[iPlayer] = NULL;
		playersTouchingCount[iPlayer] = 0;
	}

	totalTouchingPlayers = 0;
}

void OMO_KingOfTheHillZone::placeArea()
{
	relocatetimer = 0;
	colorID = -1;
	iPlayerID = -1;
	frame = 0;

	multiplier = 1;
	multipliertimer = 0;

	short x;
	short y;

	for(short iLoop = 0; iLoop < 64; iLoop++)
	{
		x = (short)(rand()%(MAPWIDTH - size + 1));
		y = (short)(rand()%(MAPHEIGHT - size));

		//First move the zone down so it is sitting on atleast 1 solid tile
		short iFindY = y + size;
		short iOldFindY = iFindY;
		bool fTryAgain = false;
		bool fDone = false;

		while(!fDone)
		{
			for(short iCol = 0; iCol < size; iCol++)
			{
				int type = g_map.map(x + iCol, iFindY);
				if(((type & tile_flag_solid_on_top || type & tile_flag_solid) && (type & tile_flag_death_on_top) == 0) || g_map.block(x + iCol, iFindY))
				{
					fDone = true;
					break;
				}
			}

			if(fDone)
				break;

			if(++iFindY >= MAPHEIGHT)
				iFindY = size;

			if(iFindY == iOldFindY)  //If we didn't find solid ground in that loop, look for a new place for the zone
			{
				fTryAgain = true;
				break;
			}
		}

		if(fTryAgain)
			continue;

		y = iFindY - size;
	
		//Now verify that the area is not completely covered with solid tiles
		short iCountSolidTiles = 0;
		for(short iRow = 0; iRow < size; iRow++)
		{
			for(short iCol = 0; iCol < size; iCol++)
			{
				//If there is a solid tile inside the zone
				if((g_map.map(x + iCol, y + iRow) & tile_flag_solid) || !g_map.spawn(1, x + iCol, y + iRow) || g_map.block(x + iCol, y + iRow))
				{
					iCountSolidTiles++;

					//Be more picky in the first few loops, but allow solid tiles to be in
					if((iLoop < 16 && iCountSolidTiles > iKingOfTheHillZoneLimits[size - 2][0]) || (iLoop < 32 && iCountSolidTiles > iKingOfTheHillZoneLimits[size - 2][1]) || 
						(iLoop < 48 && iCountSolidTiles > iKingOfTheHillZoneLimits[size - 2][2]) || (iLoop < 63 && iCountSolidTiles > iKingOfTheHillZoneLimits[size - 2][3]))
					{
						fTryAgain = true;
						break;
					}
				}
			}

			if(fTryAgain)
				break;
		}

		if(fTryAgain)
			continue;

		//Verify zone is not in a platform
		if(g_map.IsInPlatformNoSpawnZone(x << 5, y << 5, size << 5, size << 5))
			continue;

		break;
	}

	ix = x << 5;
	iy = y << 5;
}

void OMO_KingOfTheHillZone::reset()
{
	iPlayerID = -1;
	colorID = -1;
	scoretimer = 0;
	frame = 0;
}

//------------------------------------------------------------------------------
// class race goal (for Race mode)
//------------------------------------------------------------------------------
short flagpositions[3][4][2] = { { {18, 20}, {34, 20}, {0, 0}, {0, 0} },
							{ {18, 20}, {34, 20}, {26, 36}, {0, 0} },
							{ {18, 20}, {34, 20}, {18, 36}, {34, 36} } };

OMO_RaceGoal::OMO_RaceGoal(gfxSprite *nspr, short id) :
	IO_OverMapObject(nspr, 0, 0, 2, 8)
{
	iw = (short)spr->getWidth() >> 1;
	ih = (short)spr->getHeight() >> 1;
	collisionWidth = 36;
	collisionHeight = 36;
	collisionOffsetX = 16;
	collisionOffsetY = 18;

	objectType = object_race_goal;
	state = 1;

	goalID = id;

	for(short k = 0; k < 4; k++)
		tagged[k] = -1;

	angle = (float)(rand()%1000 * 0.00628f);
	anglechange = (float)(rand()%100 * 0.0002f);
	anglechangetimer = (short)(rand() % 50 + 100);

	velx = sin(angle);
	vely = cos(angle);

	placeRaceGoal();

	speed = (float)game_values.gamemodesettings.race.speed / 4.0f;
	quantity = game_values.gamemodesettings.race.quantity;
	isfinishline = goalID == quantity - 1;
}

bool OMO_RaceGoal::collide(CPlayer * player)
{
	if(game_values.gamemode->getgamemode() == game_mode_race && player->statue_timer == 0)
	{
		CGM_Race * gamemode = (CGM_Race*)game_values.gamemode;
	
		if(tagged[player->teamID] != player->colorID && gamemode->getNextGoal(player->teamID) >= goalID)
		{
			tagged[player->teamID] = player->colorID;
			
			if(isfinishline)
                ifsoundonplay(sfx_racesound);
			else
				ifsoundonplay(sfx_areatag);
		}

		if(gamemode->getNextGoal(player->teamID) == goalID)
			gamemode->setNextGoal(player->teamID);
	}
	return false;
}

void OMO_RaceGoal::draw()
{
	if(isfinishline)
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, 54, iw, ih);	
	}
	else
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, 0, iw, ih);	

		for(short k = 0; k < score_cnt; k++)
		{
			if(tagged[k] > -1)
				spr_bonus.draw(ix - collisionOffsetX + flagpositions[score_cnt - 2][k][0], iy - collisionOffsetY + flagpositions[score_cnt - 2][k][1], 0, tagged[k] * 16, 16, 16);
		}
	}

	spr_racetext.draw(ix - collisionOffsetX + 26, iy - collisionOffsetY, (goalID + 1) * 16, 0, 16, 16);
}

void OMO_RaceGoal::update()
{
	if(--anglechangetimer <= 0)
	{
		anglechange = (float)((rand()%101 - 50) * 0.0002f);
		anglechangetimer = (short)(rand() % 50 + 100);
	}
	
	angle += anglechange;

	velx = speed * sin(angle);
	vely = speed * cos(angle);
	
	IO_OverMapObject::update();

	ix = (short)fx;
	iy = (short)fy;

	if(ix < 0)
	{
		velx = -velx;
		ix = 0;
		fx = (float)ix;

		angle = atan2(velx, vely);
	}
	else if(ix + collisionWidth >= 640)
	{
		velx = -velx;
		ix = 639 - collisionWidth;
		fx = (float)ix;

		angle = atan2(velx, vely);
	}
	
	if(iy < 0)
	{
		vely = -vely;
		iy = 0;
		fy = (float)iy;

		angle = atan2(velx, vely);
	}
	else if(iy + collisionHeight >= 480)
	{
		vely = -vely;
		iy = 479 - collisionHeight;
		fy = (float)iy;

		angle = atan2(velx, vely);
	}
}

void OMO_RaceGoal::placeRaceGoal()
{
	short x = 0, y = 0;

	if(goalID < g_map.iNumRaceGoals)
	{
		x = g_map.racegoallocations[goalID].x;
		y = g_map.racegoallocations[goalID].y;
	}
	else
	{
		short tries = 0;
		do
		{
			if(++tries > 32)
				break;

			x = (short)(rand() % (640 - collisionWidth));
			y = (short)(rand() % (480 - collisionHeight));
		}
		while(objectcontainer[2].getClosestObject(x, y, object_race_goal) <= 250.0f - (quantity * 25.0f));
	}

	xi(x);
	yi(y);
}

//------------------------------------------------------------------------------
// class frenzycard (for fire frenzy mode)
//------------------------------------------------------------------------------
MO_FrenzyCard::MO_FrenzyCard(gfxSprite *nspr, short iType) :
	IO_MovingObject(nspr, 0, 0, 12, 8, -1, -1, -1, -1, 0, iType * 32, 32, 32)
{
	state = 1;
	objectType = object_frenzycard;
	type = iType;

	if(type == NUMFRENZYCARDS - 1)
		type = rand() % (NUMFRENZYCARDS - 1);
	
	sparkleanimationtimer = 0;
	sparkledrawframe = 0;

	placeCard();

	fObjectCollidesWithMap = false;
}

bool MO_FrenzyCard::collide(CPlayer * player)
{
	if(type < 14 || type > 17 || game_values.gamemodesettings.frenzy.storedshells)
	{
		player->SetPowerup(type);
		game_values.gamemode->frenzyowner = player;
	}
	else
	{
		switch(type)
		{
			case 14:
			{
				CO_Shell * shell = new CO_Shell(0, 0, 0, true, true, true, false); 
				objectcontainer[1].add(shell);
				shell->UsedAsStoredPowerup(player);
				break;
			}
			case 15:
			{
				CO_Shell * shell = new CO_Shell(1, 0, 0, false, true, true, false);
				objectcontainer[1].add(shell);
				shell->UsedAsStoredPowerup(player);
				break;
			}
			case 16:
			{
				CO_Shell * shell = new CO_Shell(2, 0, 0, false, false, true, true); 
				objectcontainer[1].add(shell);
				shell->UsedAsStoredPowerup(player);
				break;
			}
			case 17:
			{
				CO_Shell * shell = new CO_Shell(3, 0, 0, false, true, false, false); 
				objectcontainer[1].add(shell);
				shell->UsedAsStoredPowerup(player);
				break;
			}
		}
	}

	dead = true;
	return false;
}

void MO_FrenzyCard::update()
{
	animate();

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}

	if(++timer > 1500)
		placeCard();
}

void MO_FrenzyCard::draw()
{
	IO_MovingObject::draw();
	
	//Draw sparkles
	spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
}

void MO_FrenzyCard::placeCard()
{
	timer = 0;

	short x = 0, y = 0;
	short iAttempts = 32;
	while((!g_map.findspawnpoint(5, &x, &y, collisionWidth, collisionHeight, false) ||
		objectcontainer[1].getClosestObject(x, y, object_frenzycard) <= 150.0f)
		&& iAttempts-- > 0);

	xi(x);
	yi(y);
}

//------------------------------------------------------------------------------
// class collection card (for card collection mode)
//------------------------------------------------------------------------------
MO_CollectionCard::MO_CollectionCard(gfxSprite *nspr, short iType, short iValue, short iUncollectableTime, float dvelx, float dvely, short ix, short iy) :
	IO_MovingObject(nspr, ix, iy, 6, 8, -1, -1, -1, -1, 0, 0, 32, 32)
{
	state = 1;
	objectType = object_moving;
	movingObjectType = movingobject_collectioncard;
	
	sparkleanimationtimer = 0;
	sparkledrawframe = 0;

	type = iType;
	value = iValue;

	uncollectabletime = iUncollectableTime;
	velx = dvelx;
	vely = dvely;
	
	if(iType == 0)
	{
		placeCard();
		fObjectCollidesWithMap = false;
	}
	else
	{
		collision_detection_checksides();
		animationOffsetY = (value + 1) << 5;
	}
}

bool MO_CollectionCard::collide(CPlayer * player)
{
	//If it is not collectable, return
	if((type == 1 && uncollectabletime > 0) || state != 1)
		return false;

	ifsoundonplay(sfx_areatag);

	//Add this card to the team's score
	if(player->score->subscore[0] < 3)
	{
		player->score->subscore[1] |= value << (player->score->subscore[0] << 1);
		player->score->subscore[0]++;
	}
	else
	{
		player->score->subscore[1] &= ~48; //Clear previous card in 3rd slot
		player->score->subscore[1] |= value << 4; //Set card to newly collected one in 3rd slot
	}

	player->score->subscore[2] = 0;

	if(type == 1)
	{
		dead = true;
	}
	else
	{
		state = 2;
		animationspeed = 4;
		animationtimer = 0;
		animationOffsetY = animationOffsetY = (value + 1) << 5;
		drawframe = 96;
	}

	timer = 0;

	return false;
}

void MO_CollectionCard::update()
{
	if(type == 1 || state < 3)
		animate();
	
	//Handle flipping over a card to reveal it's value
	if(state == 2 && drawframe == 0)
	{
		state = 3;
		timer = 0;
	}
	else if(state == 3)
	{
		if(++timer > 200)
		{
			dead = true;
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix, iy, 3, 8));
		}
	}


	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}

	if(type == 0)
	{
		if(++timer > 1500)
			placeCard();
	}
	else
	{
		applyfriction();
		IO_MovingObject::update();

		if(--uncollectabletime < -game_values.gamemodesettings.collection.cardlife)
		{
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix, iy, 3, 8));
			dead = true;
		}
	}
}

void MO_CollectionCard::draw()
{
	IO_MovingObject::draw();
	
	//Draw sparkles
	spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
}

void MO_CollectionCard::placeCard()
{
	timer = 0;

	short x = 0, y = 0;
	short iAttempts = 32;
	while((!g_map.findspawnpoint(5, &x, &y, collisionWidth, collisionHeight, false) ||
		objectcontainer[1].getClosestMovingObject(x, y, movingobject_collectioncard) <= 150.0f)
		&& iAttempts-- > 0);

	xi(x);
	yi(y);
}


//------------------------------------------------------------------------------
// class walking enemy (base class for goomba and koopa)
//------------------------------------------------------------------------------
MO_WalkingEnemy::MO_WalkingEnemy(gfxSprite *nspr, short iNumSpr, short aniSpeed, short iCollisionWidth, short iCollisionHeight, short iCollisionOffsetX, short iCollisionOffsetY, short iAnimationOffsetX, short iAnimationOffsetY, short iAnimationHeight, short iAnimationWidth, bool moveToRight, bool killOnWeakWeapon, bool bouncing, bool fallOffLedges) :
	IO_MovingObject(nspr, 0, 0, iNumSpr, aniSpeed, iCollisionWidth, iCollisionHeight, iCollisionOffsetX, iCollisionOffsetY, iAnimationOffsetX, iAnimationOffsetY, iAnimationHeight, iAnimationWidth)
{
	if(moveToRight)
		velx = 1.0f;
	else
		velx = -1.0f;

	movingObjectType = movingobject_none;

	fBouncing = bouncing;
	if(fBouncing)
		bounce = -VELENEMYBOUNCE;
	else
		bounce = GRAVITATION;
	
	spawnradius = 100.0f;
	spawnangle = (float)(rand()%1000 * 0.00628f);
	inair = true;

	iSpawnIconOffset = 0;

	burnuptimer = 0;

	fKillOnWeakWeapon = killOnWeakWeapon;

	frozen = false;
	frozentimer = 0;
	frozenvelocity = velx;
	frozenanimationspeed = aniSpeed;

	fFallOffLedges = fallOffLedges;

	place();
}

void MO_WalkingEnemy::draw()
{
	if(state == 0)
	{
		short numeyecandy = 8;
		float addangle = TWO_PI / numeyecandy;
		float displayangle = spawnangle;

		for(short k = 0; k < numeyecandy; k++)
		{
			short spawnX = ix + (collisionWidth >> 1) - 8 + (short)(spawnradius * cos(displayangle));
			short spawnY = iy + (collisionHeight >> 1) - 8 + (short)(spawnradius * sin(displayangle));

			displayangle += addangle;
		
			spr_awardsouls.draw(spawnX, spawnY, iSpawnIconOffset, 0, 16, 16);
		}
	}
	else
	{
		IO_MovingObject::draw();

		if(frozen)
		{
			spr_iceblock.draw(ix - collisionOffsetX + iw - 32, iy - collisionOffsetY + ih - 32, 0, 0, 32, 32);
		}
	}
}

void MO_WalkingEnemy::update()
{
	if(frozen)
	{
		if(--frozentimer <= 0)
		{
			frozentimer = 0;
			frozen = false;
			
			velx = frozenvelocity;
			animationspeed = frozenanimationspeed;

			if(fBouncing)
				bounce = -VELENEMYBOUNCE;
		
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX + iw - 32, iy - collisionOffsetY + ih - 32, 3, 8));
		}
	}

	if(state == 0)
	{
		spawnradius -= 2.0f;
		spawnangle += 0.05f;

		if(spawnradius < 10.0f)
			state = 1;
	}
	else
	{
		IO_MovingObject::update();
	}

	//Deal with terminal burnup velocity
	if(vely >= MAXVELY)
	{
		if(++burnuptimer > 20)
		{
			if(burnuptimer > 80)
				KillObjectMapHazard();
			else
				eyecandy[0].add(new EC_SingleAnimation(&spr_burnup, ix + (collisionWidth >> 1) - 16, iy + (collisionHeight >> 1) - 16, 5, 4));
		}
	}
	else
	{
		burnuptimer = 0;
	}

	//If this enemy doesn't fall off of ledges, then take a look at the area in front of them
	//to determine if they need to turn around
	if(!inair && !fFallOffLedges)
	{		
		short probeCenterX = ix + (collisionWidth >> 1);
		short probeFrontX = ix + (velx > 0.0f ? collisionWidth + 1 : -1);
		short probeY = iy + collisionHeight + 5;

		if(platform)
		{
			int iFrontTileType = platform->GetTileTypeFromCoord(probeFrontX, probeY);
			int iCenterTileType = platform->GetTileTypeFromCoord(probeCenterX, probeY);

			bool fFrontGap = iFrontTileType == tile_flag_nonsolid || iFrontTileType == tile_flag_super_death_top;
			bool fCenterGap = iCenterTileType == tile_flag_nonsolid || iCenterTileType == tile_flag_super_death_top;

			//If there is a hole or the type will kill the enemy, then turn around
			if(fFrontGap && fCenterGap)
			{
				velx = -velx;
			}
		}
		else
		{
			if(probeFrontX >= 640)
			{
				probeFrontX -= 640;
			}
			else if(probeFrontX < 0)
			{
				probeFrontX += 640;
			}

			if(probeCenterX >= 640)
			{
				probeCenterX -= 640;
			}
			else if(probeCenterX < 0)
			{
				probeCenterX += 640;
			}

			if(probeFrontX >= 0 && probeFrontX < 640 && probeCenterX >= 0 && probeCenterX < 640 && probeY >= 0 && probeY < 480)
			{
				probeFrontX /= TILESIZE;
				probeCenterX /= TILESIZE;
				probeY /= TILESIZE;
				
				IO_Block * frontBlock = g_map.block(probeFrontX, probeY);
				IO_Block * centerBlock = g_map.block(probeCenterX, probeY);

				bool fFoundFrontBlock = frontBlock && !frontBlock->isTransparent() && !frontBlock->isHidden();
				bool fFoundCenterBlock = centerBlock && !centerBlock->isTransparent() && !centerBlock->isHidden();
				
				if(!fFoundFrontBlock && !fFoundCenterBlock)
				{
					int frontTile = g_map.map(probeFrontX, probeY);
					int centerTile = g_map.map(probeCenterX, probeY);

					bool fFrontGap = (frontTile & tile_flag_super_death_top) || (!(frontTile & tile_flag_solid) && !(frontTile & tile_flag_solid_on_top));
					bool fCenterGap = (centerTile & tile_flag_super_death_top) || (!(centerTile & tile_flag_solid) && !(centerTile & tile_flag_solid_on_top));

					if(fFrontGap && fCenterGap)
					{
						velx = -velx;
					}
				}
			}
		}
	}
}
	

bool MO_WalkingEnemy::collide(CPlayer * player)
{
	if(state == 0)
		return false;

	if(player->invincible || frozen)
	{
		AddAwardKill(player, NULL, killStyle);

		if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
			player->score->AdjustScore(1);

		if(frozen)
		{
			ShatterDie();
		}
		else
		{
			ifsoundonplay(sfx_kicksound);
			Die();
		}
	}
	else
	{
		if(player->fOldY + PH <= fOldY && player->iy + PH >= iy)
			return hittop(player);
		else
			return hitother(player);
	}

	return false;
}

bool MO_WalkingEnemy::hitother(CPlayer * player)
{
	if(player->shield > 0)
		return false;

	return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;
}

void MO_WalkingEnemy::collide(IO_MovingObject * object)
{
	if(state == 0)
		return;

	if(!object->GetDead())
	{
		removeifprojectile(object, false, false);

		MovingObjectType type = object->getMovingObjectType();

		if(((type == movingobject_fireball || type == movingobject_hammer || type == movingobject_boomerang) && (fKillOnWeakWeapon || frozen)) || type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_bulletbill || type == movingobject_podobo || type == movingobject_attackzone || type == movingobject_explosion || type == movingobject_sledgehammer)
		{
			//Don't kill enemies with non-moving shells
			if(type == movingobject_shell && object->state == 2)
				return;

			//Don't kill enemies with slow or non-moving boxes
			if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
				return;

			if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
			{
				//Find the player that shot this fireball so we can attribute a kill
				CPlayer * killer = GetPlayerFromGlobalID(object->iPlayerID);

				if(killer)
				{
					AddAwardKill(killer, NULL, killStyle);
					killer->score->AdjustScore(1);

					if(type == movingobject_shell)
						((CO_Shell*)object)->AddMovingKill(killer);
				}
			}

			if(frozen)
			{
				ShatterDie();
			}
			else
			{
				ifsoundonplay(sfx_kicksound);
				
				if(type == movingobject_attackzone)
					DieAndDropShell(true, false);
				else
					Die();
			}

			if(type == movingobject_shell || type == movingobject_throwblock)
			{
				object->CheckAndDie();
			}
			else if(type == movingobject_bulletbill || type == movingobject_attackzone || type == movingobject_throwbox)
			{
				object->Die();
			}			
		}
		else if(type == movingobject_iceblast)
		{
			frozenvelocity = velx;
			velx = 0.0f;
			animationspeed = 0;
			
			bounce = GRAVITATION;

			frozen = true;
			frozentimer = 300;
			
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX + iw - 32, iy - collisionOffsetY + ih - 32, 3, 8));
		}
	}
}


void MO_WalkingEnemy::place()
{
	short iAttempts = 10;
	while(!g_map.findspawnpoint(5, &ix, &iy, collisionWidth, collisionHeight, false) && iAttempts-- > 0);
	fx = (float)ix;
	fy = (float)iy;
}

void MO_WalkingEnemy::ShatterDie()
{
	ifsoundonplay(sfx_breakblock);
	dead = true;

	short iBrokenIceX = ix - collisionOffsetX + iw - 32, iBrokenIceY = iy - collisionOffsetY + ih - 32;
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX, iBrokenIceY, -1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX + 16, iBrokenIceY, 1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX, iBrokenIceY + 16, -1.5f, -4.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX + 16, iBrokenIceY + 16, 1.5f, -4.0f, 4, 2, 0, 0, 16, 16));

	game_values.unlocksecret2part2++;
}


//------------------------------------------------------------------------------
// class goomba
//------------------------------------------------------------------------------
MO_Goomba::MO_Goomba(gfxSprite *nspr, bool moveToRight, bool fBouncing) :
	MO_WalkingEnemy(nspr, 2, 8, 30, 20, 1, 11, 0, moveToRight ? 0 : 32, 32, 32, moveToRight, true, fBouncing, true)
{
	movingObjectType = movingobject_goomba;
	iSpawnIconOffset = 64;
	killStyle = kill_style_goomba;

	if(fBouncing)
	{
		iw = 40;
		ih = 48;

		fOldY = fy - ih;

		collisionOffsetX = 5;
		collisionOffsetY = 27;

		animationOffsetY = moveToRight ? 0 : ih;
	}
}

void MO_Goomba::draw()
{
	//if frozen, just draw shell, not entire koopa
	if(frozen)
	{
		spr_goomba.draw(ix - collisionOffsetX + iw - 32, iy - collisionOffsetY + ih - 32, 0, 0, 32, 32);	
		spr_iceblock.draw(ix - collisionOffsetX + iw - 32, iy - collisionOffsetY + ih - 32, 0, 0, 32, 32);	
	}
	else
	{
		MO_WalkingEnemy::draw();
	}
}

void MO_Goomba::update()
{
	if(velx < 0.0f)
		animationOffsetY = ih;
	else
		animationOffsetY = 0;

	MO_WalkingEnemy::update();
}
	
bool MO_Goomba::hittop(CPlayer * player)
{
	player->yi(iy - PH - 1);
	player->bouncejump();
	player->collision_detection_checktop();
	player->platform = NULL;

	if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
		player->score->AdjustScore(1);

	if(fBouncing)
	{
		fBouncing = false;
		bounce = GRAVITATION;

		if(vely < GRAVITATION)
			vely = GRAVITATION;

		iw = 32;
		ih = 32;

		collisionOffsetX = 1;
		collisionOffsetY = 11;

		animationWidth = 64;
		drawframe = 0;

		animationOffsetY = velx > 0.0f ? 0 : ih;
		spr = &spr_goomba;
	}
	else
	{
		dead = true;

		AddAwardKill(player, NULL, killStyle);

		eyecandy[0].add(new EC_Corpse(&spr_goombadead, (float)(ix - collisionOffsetX), (float)(iy + collisionHeight - 32), 0));
	}

	ifsoundonplay(sfx_mip);

	return false;
}


void MO_Goomba::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	dead = true;
	eyecandy[2].add(new EC_FallingObject(&spr_goombadeadflying, ix, iy, 0.0f, -VELJUMP / 2.0f, 1, 0, 0, 0, 0, 0));
}

//------------------------------------------------------------------------------
// class koopa
//------------------------------------------------------------------------------
MO_Koopa::MO_Koopa(gfxSprite *nspr, bool moveToRight, bool red, bool fBouncing, bool fFallOffLedges) :
	MO_WalkingEnemy(nspr, 2, 8, 30, 28, 1, 25, 0, moveToRight ? 0 : 54, 54, 32, moveToRight, true, fBouncing, fFallOffLedges)
{
	fRed = red;
	movingObjectType = movingobject_koopa;
	iSpawnIconOffset = fRed ? 144 : 112;
	killStyle = kill_style_koopa;
}

void MO_Koopa::draw()
{
	//if frozen, just draw shell, not entire koopa
	if(frozen)
	{
		spr_shell.draw(ix - collisionOffsetX + iw - 32, iy - collisionOffsetY + ih - 32, 0, fRed ? 32 : 0, 32, 32);	
		spr_iceblock.draw(ix - collisionOffsetX + iw - 32, iy - collisionOffsetY + ih - 32, 0, 0, 32, 32);	
	}
	else
	{
		MO_WalkingEnemy::draw();
	}
}

void MO_Koopa::update()
{
	if(velx < 0.0f)
		animationOffsetY = 54;
	else
		animationOffsetY = 0;

	MO_WalkingEnemy::update();
}

bool MO_Koopa::hittop(CPlayer * player)
{
	player->yi(iy - PH - 1);
	player->bouncejump();
	player->collision_detection_checktop();
	player->platform = NULL;
	
	if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
		player->score->AdjustScore(1);

	if(fBouncing)
	{
		fBouncing = false;
		bounce = GRAVITATION;

		if(vely < GRAVITATION)
			vely = GRAVITATION;

		spr = fRed ? &spr_redkoopa : &spr_koopa;
	}
	else
	{
		dead = true;

		AddAwardKill(player, NULL, kill_style_koopa);

		DropShell(false, false);
	}

	ifsoundonplay(sfx_mip);

	return false;
}

void MO_Koopa::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	dead = true;
	eyecandy[2].add(new EC_FallingObject(&spr_shelldead, ix, iy, 0.0f, -VELJUMP / 2.0f, 1, 0, fRed ? 32 : 0, 0, 32, 32));
}

void MO_Koopa::DropShell(bool fBounce, bool fFlip)
{
	//Give the shell a state 2 so it is already spawned but sitting
	CO_Shell * shell;
	
	if(fRed)
		shell = new CO_Shell(1, ix - 1, iy + 8, false, true, true, false);
	else
		shell = new CO_Shell(0, ix - 1, iy + 8, true, true, true, false);
	
	shell->nospawn(iy + 8, fBounce);

	if(fFlip)
		shell->Flip();

	objectcontainer[1].add(shell);
}

//------------------------------------------------------------------------------
// class buzzy beetle
//------------------------------------------------------------------------------
MO_BuzzyBeetle::MO_BuzzyBeetle(gfxSprite *nspr, bool moveToRight) :
	MO_WalkingEnemy(nspr, 2, 8, 30, 20, 1, 11, 0, moveToRight ? 0 : 32, 32, 32, moveToRight, false, false, true)
{
	movingObjectType = movingobject_buzzybeetle;
	iSpawnIconOffset = 160;
	killStyle = kill_style_buzzybeetle;
}

void MO_BuzzyBeetle::update()
{
	if(velx < 0.0f)
		animationOffsetY = 32;
	else
		animationOffsetY = 0;

	MO_WalkingEnemy::update();
}

bool MO_BuzzyBeetle::hittop(CPlayer * player)
{
	player->yi(iy - PH - 1);
	player->bouncejump();
	player->collision_detection_checktop();
	player->platform = NULL;
	dead = true;

	AddAwardKill(player, NULL, kill_style_buzzybeetle);

	if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
		player->score->AdjustScore(1);

	ifsoundonplay(sfx_mip);

	DropShell(false, false);
	
	return false;
}

void MO_BuzzyBeetle::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	dead = true;
	eyecandy[2].add(new EC_FallingObject(&spr_shelldead, ix, iy, 0.0f, -VELJUMP / 2.0f, 1, 0, 96, 0, 32, 32));
}

void MO_BuzzyBeetle::DropShell(bool fBounce, bool fFlip)
{
	//Give the shell a state 2 so it is already spawned but sitting
	CO_Shell * shell = new CO_Shell(3, ix - 1, iy, false, true, false, false);
	shell->nospawn(iy, fBounce);
	
	if(fFlip)
		shell->Flip();

	objectcontainer[1].add(shell);
}

//------------------------------------------------------------------------------
// class spiny
//------------------------------------------------------------------------------
MO_Spiny::MO_Spiny(gfxSprite *nspr, bool moveToRight) :
	MO_WalkingEnemy(nspr, 2, 8, 30, 20, 1, 11, 0, moveToRight ? 0 : 32, 32, 32, moveToRight, true, false, true)
{
	movingObjectType = movingobject_spiny;
	iSpawnIconOffset = 176;
	killStyle = kill_style_spiny;
}

void MO_Spiny::update()
{
	if(velx < 0.0f)
		animationOffsetY = 32;
	else
		animationOffsetY = 0;

	MO_WalkingEnemy::update();
}

bool MO_Spiny::hittop(CPlayer * player)
{
	//Kill player here
	if(player->isready() && player->shield == 0 && !player->invincible && player->iKuriboShoe == 0)
		return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;

	if(player->iKuriboShoe > 0)
	{
		player->yi(iy - PH - 1);
		player->bouncejump();
		player->collision_detection_checktop();
		player->platform = NULL;
		
		dead = true;

		AddAwardKill(player, NULL, kill_style_spiny);

		if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
			player->score->AdjustScore(1);

		DropShell(false, false);

		ifsoundonplay(sfx_mip);
	}
		
	return false;
}

void MO_Spiny::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	dead = true;
	eyecandy[2].add(new EC_FallingObject(&spr_shelldead, ix, iy, 0.0f, -VELJUMP / 2.0f, 1, 0, 64, 0, 32, 32));
}

void MO_Spiny::DropShell(bool fBounce, bool fFlip)
{
	//Give the shell a state 2 so it is already spawned but sitting
	CO_Shell * shell = new CO_Shell(2, ix - 1, iy, false, true, false, false);
	shell->nospawn(iy, fBounce);

	if(fFlip)
		shell->Flip();

	objectcontainer[1].add(shell);
}

//------------------------------------------------------------------------------
// class cheep cheep
//------------------------------------------------------------------------------
MO_CheepCheep::MO_CheepCheep(gfxSprite *nspr) :
	IO_MovingObject(nspr, 0, 480, 2, 8, 30, 28, 1, 3)
{
	ih = 32;
	xi((short)(rand() % 608));
	
	velx = 0.0f;
	while(velx == 0.0f)
		velx = float(rand() % 19 - 9) / 2.0f;
	
	//Cheep cheep up velocity is between 9.0 and 13.0 in 0.5 increments
	vely = -(float(rand() % 11) / 2.0f) - 9.0f;

	movingObjectType = movingobject_cheepcheep;
	state = 1;

	iColorOffsetY = (short)(rand() % 3) * 64;
	
	if(velx > 0.0f)
		iColorOffsetY += 32;

	fObjectCollidesWithMap = false;
	frozen = false;
}

void MO_CheepCheep::update()
{
	fOldX = fx;
	fOldY = fy;

	xf(fx + velx);
	yf(fy + vely);

	//Cheep cheep gravitation
	vely += 0.2f;

	animate();

	//Remove if cheep cheep has fallen below bottom of screen
	if(vely > 0.0f && iy > 480)
		dead = true;
}

void MO_CheepCheep::draw()
{
	spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iColorOffsetY, iw, ih);

	if(frozen)
	{
		spr_iceblock.draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, 0, 32, 32);
	}
}

bool MO_CheepCheep::collide(CPlayer * player)
{
	if(player->invincible || frozen)
	{
		AddAwardKill(player, NULL, kill_style_cheepcheep);

		if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
			player->score->AdjustScore(1);
		
		if(frozen)
		{
			ShatterDie();
		}
		else
		{
			ifsoundonplay(sfx_kicksound);
			Die();
		}
	}
	else
	{
		if(player->fOldY + PH <= fOldY && player->iy + PH >= iy)
			return hittop(player);
		else
			return hitother(player);
	}

	return false;
}


bool MO_CheepCheep::hittop(CPlayer * player)
{
	player->yi(iy - PH - 1);
	player->bouncejump();
	player->collision_detection_checktop();
	player->platform = NULL;
	
	AddAwardKill(player, NULL, kill_style_cheepcheep);

	if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
		player->score->AdjustScore(1);

	ifsoundonplay(sfx_mip);
	
	Die();

	return false;
}

bool MO_CheepCheep::hitother(CPlayer * player)
{
	if(player->shield > 0)
		return false;

	return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;
}

void MO_CheepCheep::collide(IO_MovingObject * object)
{
	if(!object->GetDead())
	{
		removeifprojectile(object, false, false);

		MovingObjectType type = object->getMovingObjectType();

		if(type == movingobject_fireball || type == movingobject_hammer || type == movingobject_boomerang || type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_bulletbill || type == movingobject_podobo || type == movingobject_attackzone || type == movingobject_explosion || type == movingobject_sledgehammer)
		{
			//Don't kill goombas with non-moving shells
			if(type == movingobject_shell && object->state == 2)
				return;

			if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
				return;

			if(game_values.gamemode->gamemode == game_mode_stomp && !game_values.gamemode->gameover)
			{
				//Find the player that shot this projectile so we can attribute a kill
				CPlayer * killer = GetPlayerFromGlobalID(object->iPlayerID);

				if(killer)
				{
					AddAwardKill(killer, NULL, kill_style_cheepcheep);
					killer->score->AdjustScore(1);

					if(type == movingobject_shell)
						((CO_Shell*)object)->AddMovingKill(killer);
				}
			}

			if(frozen)
			{
				ShatterDie();
			}
			else
			{
				ifsoundonplay(sfx_kicksound);
				Die();
			}
		
			if(type == movingobject_shell || type == movingobject_throwblock)
			{
				object->CheckAndDie();
			}
			else if(type == movingobject_bulletbill || type == movingobject_attackzone || type == movingobject_throwbox)
			{
				object->Die();
			}
		}
		else if(type == movingobject_iceblast)
		{
			animationspeed = 0;
			frozen = true;
			
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX, iy - collisionOffsetY, 3, 8));
		}
	}
}

void MO_CheepCheep::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	dead = true;
	eyecandy[2].add(new EC_FallingObject(&spr_cheepcheepdead, ix, iy, 0.0f, -VELJUMP / 2.0f, 1, 0, 0, iColorOffsetY, 32, 32));
}

void MO_CheepCheep::ShatterDie()
{
	ifsoundonplay(sfx_breakblock);
	dead = true;

	short iBrokenIceX = ix - collisionOffsetX, iBrokenIceY = iy - collisionOffsetY;
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX, iBrokenIceY, -1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX + 16, iBrokenIceY, 1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX, iBrokenIceY + 16, -1.5f, -4.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX + 16, iBrokenIceY + 16, 1.5f, -4.0f, 4, 2, 0, 0, 16, 16));

	game_values.unlocksecret2part2++;
}

///////////////////////DEBUG!  REMOVE THIS WHEN DONE/////////////////////////////
#ifdef _DEBUG
	extern bool fDebugShowBossSettings;
#endif
///////////////////////DEBUG!  REMOVE THIS WHEN DONE/////////////////////////////

//------------------------------------------------------------------------------
// class sledge brother
//------------------------------------------------------------------------------
//{jump, throw, turn, wait, taunt} otherwise move
short g_iSledgeBrotherActions[3][5][5] = {
	{{0,50,65,75,85},{3,53,66,76,86},{5,55,70,78,83},{5,70,85,85,90},{5,75,90,90,90}},
	{{0,30,60,80,85},{5,40,65,80,85},{5,50,65,75,85},{10,55,65,70,90},{10,55,65,65,90}},
	{{0,50,65,75,85},{3,53,65,75,85},{5,60,72,80,88},{5,65,80,85,90},{5,75,90,90,90}}};

short g_iSledgeBrotherNeedAction[3][5][6] = {
	{{50,5,5,10,10,8},{30,4,5,10,10,8},{20,4,5,12,12,8},{12,4,5,15,15,10},{8,4,5,15,15,12}},
	{{50,5,5,10,15,8},{30,4,5,10,12,8},{20,4,5,12,10,8},{15,4,5,15,8,10},{10,4,5,15,6,12}},
	{{50,5,8,10,10,8},{30,5,8,12,12,10},{20,4,8,15,15,15},{15,4,8,15,15,15},{12,3,8,15,15,15}}};

short g_iSledgeBrotherMaxAction[3][5][5] = {
	{{1,1,1,2,2},{1,2,1,2,2},{1,3,1,2,2},{1,3,1,1,1},{1,3,1,0,0}},
	{{1,1,1,2,2},{1,2,1,2,2},{1,3,1,2,2},{1,3,1,1,1},{1,3,1,0,0}},
	{{1,1,1,2,2},{1,2,1,2,2},{1,2,1,1,1},{1,3,1,1,1},{1,3,1,0,0}}};

short g_iSledgeBrotherWaitTime[3][5][2] = {
	{{30,50},{25,45},{20,40},{15,30},{10,20}},
	{{30,50},{25,45},{20,40},{15,30},{10,20}},
	{{30,50},{25,45},{20,40},{15,30},{10,20}}};

MO_SledgeBrother::MO_SledgeBrother(gfxSprite *nspr, short platformY, short type) :
	IO_MovingObject(nspr, 0, 0, 8, 0, 32, 56, 8, 8)
{
	iType = type;
	state = 1;
	iActionState = 0;
	location = 2;

	ih = spr->getHeight() / 3;

	movingObjectType = movingobject_sledgebrother;
	
	inair = true;
	throwing_timer = 0;
	
	hit_timer = 0;
	hit_movement_timer = 0;
	hit_offset_y = iType * 64;

	leg_offset_x = 0;
	leg_movement_timer = 0;

	arm_offset_x = 0;
	arm_movement_timer = 0;

	taunt_timer = 0;

	wait_timer = 0;

	hit_points = game_values.gamemodesettings.boss.hitpoints;
	face_right = false;

	vely = 0.0f;
	velx = 0.0f;
	
	iPlatformY = platformY - collisionHeight;
	yi(iPlatformY);

	for(short iLocation = 0; iLocation < 5; iLocation++)
		iDestLocationX[iLocation] = 84 * iLocation + 128;

	iDestX = iDestLocationX[location];
	xi(iDestX);

	for(short iAction = 0; iAction < 6; iAction++)
		need_action[iAction] = 0;

	last_action = -1;
	last_action_count = 0;
}

extern short iSledgeBrotherAttribute;
void MO_SledgeBrother::draw()
{
	spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, leg_offset_x + arm_offset_x + (face_right ? 0 : 192), hit_offset_y, iw, ih);

	if(hit_timer != 0)
	{
		for(short iHeart = 0; iHeart < hit_points; iHeart++)
			spr_scorehearts.draw(ix - collisionOffsetX + iHeart * 8, iy - collisionOffsetY - 18, 0, 0, 16, 16);
	}
}

void MO_SledgeBrother::update()
{
	if(iActionState == 0)
	{
		if(hit_timer <= 0)
		{
			randomaction();
		}
	}
	else if(iActionState == 1)
	{
		if(--wait_timer <= 0)
			iActionState = 0;
	}
	else if(iActionState == 2)
	{
		yf(fy + vely);
		vely += GRAVITATION;

		if(iy >= iPlatformY)
		{
			iActionState = 0;
			
			if(iType == 0)
			{
				//Shake screen and kill players
				ifsoundonplay(sfx_thunder);
				game_values.screenshaketimer = 20;
				game_values.screenshakeplayerid = -1;
				game_values.screenshaketeamid = -1;
				game_values.screenshakekillinair = false;
				game_values.screenshakekillscount = 0;
			}
			else if(iType == 1)
			{
				//Spawn thwomps
				ifsoundonplay(sfx_thunder);

				short numThwomps = rand() % 5 + 6;

				for(short iThwomp = 0; iThwomp < numThwomps; iThwomp++)
				{
					objectcontainer[2].add(new OMO_Thwomp(&spr_thwomp, (short)(rand() % 591), 2.0f + (float)(rand()%20)/10.0f));
				}

				//short numBombs = rand() % 5 + 6;

				//for(short iBomb = 0; iBomb < numBombs; iBomb++)
				//{
				//	short iRandomX = rand() % 612;
				//	short iRandomY = rand() % 442;
				//	eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, iRandomX - 2, iRandomY + 3, 3, 8));
				//	objectcontainer[2].add(new CO_Bomb(&spr_bomb, iRandomX, iRandomY, 0.0f, 0.0f, 4, -1, -1, -1, rand() % 30 + 30));
				//}
			}
			else if(iType == 2)
			{
				//Spawn lots of podobos
				ifsoundonplay(sfx_thunder);

				short numPodobos = rand() % 5 + 8;

				for(short iPodobo = 0; iPodobo < numPodobos; iPodobo++)
				{
					objectcontainer[2].add(new MO_Podobo(&spr_podobo, (short)(rand() % 608), 480, -(float(rand() % 9) / 2.0f) - 9.0f, -1, -1, -1, false));
				}
			}
		}
	}
	else if(iActionState == 3)
	{
		if(--throwing_timer <= 0)
		{
			iActionState = 0;
			throwing_timer = 0;
			arm_offset_x = 0;
		}
	}
	else if(iActionState == 4)
	{
		//move towards destination
		if(ix < iDestX)
		{
			ix += game_values.gamemodesettings.boss.difficulty >= 3 ? 2 : 1;

			if(ix >= iDestX)
			{
				ix = iDestX;
				iActionState = 0;
				leg_offset_x = 0;
				leg_movement_timer = 0;
			}
		}
		else if(ix > iDestX)
		{
			ix -= game_values.gamemodesettings.boss.difficulty >= 3 ? 2 : 1;

			if(ix <= iDestX)
			{
				ix = iDestX;
				iActionState = 0;
				leg_offset_x = 0;
				leg_movement_timer = 0;
			}
		}

		if(iActionState != 0 && ++leg_movement_timer == 8)
		{
			leg_movement_timer = 0;
			
			if(leg_offset_x == 0)
				leg_offset_x = 48;
			else
				leg_offset_x = 0;
		}
	}
	else if(iActionState == 5)
	{
		//If we are done taunting, reset arm/legs back to normal state
		if(--taunt_timer <= 0)
		{
			iActionState = 0;
			arm_offset_x = 0;
			leg_offset_x = 0;
			arm_movement_timer = 0;
			leg_movement_timer = 0;
		}
		else  //otherwise move them around
		{
			if(++arm_movement_timer == 8)
			{
				arm_movement_timer = 0;
				
				if(arm_offset_x == 0)
					arm_offset_x = 96;
				else
					arm_offset_x = 0;
			}

			if(++leg_movement_timer == 6)
			{
				leg_movement_timer = 0;
				
				if(leg_offset_x == 0)
					leg_offset_x = 48;
				else
					leg_offset_x = 0;
			}
		}
	}

	if(hit_timer > 0)
	{
		if(--hit_timer <= 0)
		{
			hit_offset_y = iType * 64;
			hit_timer = 0;
		}
		else
		{
			if(++hit_movement_timer == 2)
			{
				hit_movement_timer = 0;
				
				hit_offset_y += 64;

				if(hit_offset_y >= 192)
					hit_offset_y = 0;
			}
		}
	}
}

void MO_SledgeBrother::randomaction()
{
	int randaction = rand() % 100;

	//Force an action if it has been too long since the last action of that type
	if(need_action[0] > g_iSledgeBrotherNeedAction[iType][game_values.gamemodesettings.boss.difficulty][0])
		jump();
	else if(need_action[1] > g_iSledgeBrotherNeedAction[iType][game_values.gamemodesettings.boss.difficulty][1])
		throwprojectile();
	else if(need_action[2] > g_iSledgeBrotherNeedAction[iType][game_values.gamemodesettings.boss.difficulty][2])
		turn();
	else if(need_action[3] > g_iSledgeBrotherNeedAction[iType][game_values.gamemodesettings.boss.difficulty][3])
		wait(g_iSledgeBrotherWaitTime[iType][game_values.gamemodesettings.boss.difficulty][0], g_iSledgeBrotherWaitTime[iType][game_values.gamemodesettings.boss.difficulty][1]);
	else if(need_action[4] > g_iSledgeBrotherNeedAction[iType][game_values.gamemodesettings.boss.difficulty][4])
		taunt();
	else if(need_action[5] > g_iSledgeBrotherNeedAction[iType][game_values.gamemodesettings.boss.difficulty][5])
		move();
	//then do action based on probability
	else if((last_action != 0 || last_action_count < g_iSledgeBrotherMaxAction[iType][game_values.gamemodesettings.boss.difficulty][0]) && 
		randaction < g_iSledgeBrotherActions[iType][game_values.gamemodesettings.boss.difficulty][0])
		jump();
	else if((last_action != 1 || last_action_count < g_iSledgeBrotherMaxAction[iType][game_values.gamemodesettings.boss.difficulty][1]) && 
		randaction < g_iSledgeBrotherActions[iType][game_values.gamemodesettings.boss.difficulty][1])
		throwprojectile();
	else if((last_action != 2 || last_action_count < g_iSledgeBrotherMaxAction[iType][game_values.gamemodesettings.boss.difficulty][2]) && 
		randaction < g_iSledgeBrotherActions[iType][game_values.gamemodesettings.boss.difficulty][2])
		turn();
	else if((last_action != 2 || last_action_count < g_iSledgeBrotherMaxAction[iType][game_values.gamemodesettings.boss.difficulty][3]) && 
		randaction < g_iSledgeBrotherActions[iType][game_values.gamemodesettings.boss.difficulty][3])
		wait(g_iSledgeBrotherWaitTime[iType][game_values.gamemodesettings.boss.difficulty][0], g_iSledgeBrotherWaitTime[iType][game_values.gamemodesettings.boss.difficulty][1]);
	else if((last_action != 2 || last_action_count < g_iSledgeBrotherMaxAction[iType][game_values.gamemodesettings.boss.difficulty][4]) && 
		randaction < g_iSledgeBrotherActions[iType][game_values.gamemodesettings.boss.difficulty][4])
		taunt();
	else
		move();

	for(short iAction = 0; iAction < 6; iAction++)
		need_action[iAction]++;
}

void MO_SledgeBrother::move()
{
	bool moveright = true;
	if(location == 0)
		moveright = true;
	else if(location == 4)
		moveright = false;
	else
		moveright = rand() % 2 == 0;

	SetLastAction(3);

	if(moveright)
		location++;
	else
		location--;

	iDestX = iDestLocationX[location];
	iActionState = 4;

	if(iDestX > ix)
		face_right = true;
	else
		face_right = false;

	need_action[5] = 0;
}

void MO_SledgeBrother::throwprojectile()
{
	SetLastAction(1);

	throwing_timer = 20;
	iActionState = 3;
	arm_offset_x = 96;

	if(iType == 0)
	{
		float fHammerVelX = ((float)(rand() % 9 + 2)) / 2.0f - (face_right ? 0.0f : 6.0f);
		objectcontainer[2].add(new MO_SledgeHammer(&spr_sledgehammer, (face_right ? ix + 32 : ix) - collisionOffsetX, iy, 8, fHammerVelX, -HAMMERTHROW, 5, -1, -1, -1, false));
	}
	else if(iType == 1)
	{
		float fBombVelX = ((float)(rand() % 5 + 12)) / 2.0f - (face_right ? 0.0f : 14.0f);
		float fBombVelY = -(float)(rand() % 13) / 2.0f - 6.0f;
		objectcontainer[2].add(new CO_Bomb(&spr_bomb, face_right ? ix + iw - 32 : ix - 20, iy, fBombVelX, fBombVelY, 4, -1, -1, -1, rand() % 60 + 120));
	}
	else if(iType == 2)
	{
		float fFireVelX = ((float)(rand() % 9 + 6)) / 2.0f - (face_right ? 0.0f : 10.0f);
		float fFireVelY = (float)(rand() % 17) / 2.0f - 4.0f;
		objectcontainer[2].add(new MO_SuperFireball(&spr_superfireball, face_right ? ix + iw - 32 : ix - 16, iy, 4, fFireVelX, fFireVelY, 4, -1, -1, -1));
	}

	need_action[1] = 0;
}

void MO_SledgeBrother::taunt()
{
	SetLastAction(2);

	ifsoundonplayloop(sfx_boomerang, 3);
	taunt_timer = 60;
	iActionState = 5;

	need_action[4] = 0;

	//If this is a bomb brother, push bombs away when taunting
	if(iType == 1)
	{
		objectcontainer[2].pushBombs(ix + 32, iy + 32);
	}
}

void MO_SledgeBrother::turn()
{
	face_right = !face_right;
	wait(g_iSledgeBrotherWaitTime[iType][game_values.gamemodesettings.boss.difficulty][0] >> 1, g_iSledgeBrotherWaitTime[iType][game_values.gamemodesettings.boss.difficulty][1] >> 1);

	need_action[2] = 0;
}

void MO_SledgeBrother::jump()
{
	SetLastAction(0);

	vely = -VELJUMP;
	iActionState = 2;

	need_action[0] = 0;
}

void MO_SledgeBrother::wait(short min, short max)
{
	SetLastAction(2);

	wait_timer = rand() % (max - min) + min;
	iActionState = 1;

	need_action[3] = 0;
}

bool MO_SledgeBrother::collide(CPlayer * player)
{
	if(iActionState == 0)
		return false;

	if(player->invincible)
	{
		ifsoundonplay(sfx_kicksound);
		Die();

		if(game_values.gamemode->gamemode == game_mode_boss_minigame)
		{
			((CGM_Boss_MiniGame*)game_values.gamemode)->SetWinner(player);
		}
	}
	else
	{
		return hit(player);
	}

	return false;
}

bool MO_SledgeBrother::hit(CPlayer * player)
{
	if(player->shield > 0)
		return false;

	return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;
}

void MO_SledgeBrother::collide(IO_MovingObject * object)
{
	if(object->GetDead())
		return;

	MovingObjectType type = object->getMovingObjectType();

	//Ignore hammers and fireballs thrown from sledge brother
	if(type == movingobject_sledgehammer || type == movingobject_superfireball)
	{
		if(object->iPlayerID == -1)
			return;
	}

	removeifprojectile(object, false, false);

	//These types of attacks damage the boss
	bool fDamageWeapon = type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_explosion;

	//These don't damage him but still collide
	bool fNoDamageWeapon = type == movingobject_fireball || type == movingobject_bulletbill || type == movingobject_hammer || type == movingobject_boomerang || type == movingobject_attackzone;

	if(fDamageWeapon || fNoDamageWeapon)
	{
		//If it is a shell but it is sitting, don't collide
		if(type == movingobject_shell && object->GetState() == 2)
			return;

		//If it is a throw box but is sitting, dont' collide
		if(type == movingobject_throwbox && !((CO_ThrowBox*)object)->HasKillVelocity())
			return;

		if(fDamageWeapon)
		{
			Damage(object->iPlayerID);

			if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox || type == movingobject_attackzone)
			{
				ifsoundonplay(sfx_kicksound);
				object->Die();
			}
		}
		else
		{
			ifsoundonplay(sfx_hit);
			
			if(type == movingobject_attackzone || type == movingobject_bulletbill)
				object->Die();
		}
	}
}

void MO_SledgeBrother::Die()
{
	dead = true;
	eyecandy[2].add(new EC_FallingObject(&spr_sledgebrothersdead, ix, iy, 0.0f, -VELJUMP / 2.0f, 1, 0, 0, iType * 64, iw, ih));
}

void MO_SledgeBrother::Damage(short playerID)
{
	if(hit_timer != 0 || playerID == -1)
		return;

	//Find the player that shot this projectile so we can attribute a kill
	CPlayer * killer = GetPlayerFromGlobalID(playerID);

	if(killer)
	{
		if(--hit_points <= 0)
		{
			Die();

			if(game_values.gamemode->gamemode == game_mode_boss_minigame)
			{
				((CGM_Boss_MiniGame*)game_values.gamemode)->SetWinner(killer);
			}
		}
		else
		{
			hit_timer = 60;
			ifsoundonplay(sfx_stun);
		}
	}
}

void MO_SledgeBrother::SetLastAction(short type)
{
	if(last_action != type)
	{
		last_action_count = 1;
		last_action = type;
	}
	else
	{
		last_action_count++;
	}
}

//------------------------------------------------------------------------------
// class shell projectile
//------------------------------------------------------------------------------
//state 0: Shell is being spawned
//state 1: Shell is moving
//state 2: Shell is waiting to be picked up
//state 3: Shell is being held
CO_Shell::CO_Shell(short type, short x, short y, bool dieOnMovingPlayerCollision, bool dieOnHoldingPlayerCollision, bool dieOnFire, bool killBouncePlayer) :
	MO_CarriedObject(&spr_shell, x, y, 4, 4, 30, 20, 1, 11, 0, type * 32, 32, 32)
{
	iShellType = type;

	state = 0;

	movingObjectType = movingobject_shell;
	
	iPlayerID = -1;
	iTeamID = -1;

	iIgnoreBounceTimer = 0;
	iBounceCounter = 0;

	fDieOnMovingPlayerCollision = dieOnMovingPlayerCollision;
	fDieOnHoldingPlayerCollision = dieOnHoldingPlayerCollision;
	fDieOnFire = dieOnFire;
	fKillBouncePlayer = killBouncePlayer;

	iDeathTime = 0;

	iDestY = iy - collisionHeight;
	fy = iDestY + 32.0f;
	iColorOffsetY = type * 32;

	iKillCounter = 0;
	iNoOwnerKillTime = 0;

	fFlipped = false;
	iFlippedOffset = 0;

	iOwnerRightOffset = 14;
	iOwnerLeftOffset = -22;
	iOwnerUpOffset = 32;

	frozen = false;
	frozentimer = 0;
	frozenvelocity = 0.0f;
	frozenanimationspeed = 4;

	sSpotlight = NULL;
}

bool CO_Shell::collide(CPlayer * player)
{
	if(player->invincible || player->shyguy || frozen)
	{
		if(frozen)
		{
			ShatterDie();
			return false;
		}
		else
		{
			if(state == 0 || state == 2) //If sitting or spawning then just die
			{
				Die();
				return false;
			}
			else if(state == 3)  //if held, but not by us then die
			{
				if(owner != player)
				{
					Die();
					return false;
				}
			}
			else if(state == 1) //If moving, see if it is actually hitting us before we kill it
			{
				short flipx = 0;

				if(player->ix + PW < 320 && ix > 320)
					flipx = 640;
				else if(ix + iw < 320 && player->ix > 320)
					flipx = -640;

				if((player->ix + HALFPW + flipx >= ix + (iw >> 1) && velx > 0.0f) || (player->ix + HALFPW + flipx < ix + (iw >> 1) && velx < 0.0f))
				{
					Die();
					return false;	
				}
			}
		}
	}

	if(player->statue_timer == 0)
	{
		if(player->fOldY + PH <= iy && player->iy + PH >= iy)
			return HitTop(player);
		else
			return HitOther(player);
	}

	return false;
}

bool CO_Shell::HitTop(CPlayer * player)
{
	if(player->invincible || player->iKuriboShoe > 0 || player->shyguy)
	{
		Die();
		fSmoking = false;
		return false;
	}

	if(fKillBouncePlayer && !fFlipped)
	{
		KillPlayer(player);
	}
	else if(state == 2) //Sitting
	{
		owner = player;
		Kick();
		fSmoking = false;
		if(player->ix + HALFPW < ix + (iw >> 1))
			velx = 5.0f;
		else
			velx = -5.0f;

		iIgnoreBounceTimer = 10;
	}
	else if(state == 1 && iIgnoreBounceTimer == 0)  //Moving
	{
		Stop();

		player->yi(iy - PH - 1);
		player->bouncejump();
		player->collision_detection_checktop();
		player->platform = NULL;
	}
	else if(state == 3) //Holding
	{
		if(player != owner && (game_values.teamcollision == 2|| player->teamID != owner->teamID))
		{
			if(owner)
				owner->carriedItem = NULL;

			Kick();
			fSmoking = false;

			player->yi(iy - PH - 1);
			player->bouncejump();
			player->collision_detection_checktop();
			player->platform = NULL;
		}
	}

	return false;
}

bool CO_Shell::HitOther(CPlayer * player)
{
	if(state == 2) //Sitting
	{
		if(owner == NULL && player->isready())
		{
			if(player->AcceptItem(this))
			{
				owner = player;
				iPlayerID = owner->globalID;
				iTeamID = owner->teamID;
				state = 3;
			}
			else
			{
				short flipx = 0;

				if(player->ix + PW < 320 && ix > 320)
					flipx = 640;
				else if(ix + iw < 320 && player->ix > 320)
					flipx = -640;

				owner = player;
				Kick();
				if(player->ix + HALFPW + flipx < ix + (iw >> 1))
					velx = 5.0f;
				else
					velx = -5.0f;
			}
		}
	}
	else if(state == 1)  //Moving
	{
		short flipx = 0;

		if(player->ix + PW < 320 && ix > 320)
			flipx = 640;
		else if(ix + iw < 320 && player->ix > 320)
			flipx = -640;

		if(iNoOwnerKillTime == 0 || player->globalID != iPlayerID || player->ix + HALFPW + flipx >= ix + (iw >> 1) && velx > 0.0f || player->ix + HALFPW + flipx < ix + (iw >> 1) && velx < 0.0f)
			return KillPlayer(player);
	}
	else if(state == 3)  //Holding
	{
		if(player != owner && (game_values.teamcollision == 2 || player->teamID != owner->teamID))
		{
			iPlayerID = owner->globalID;
			iTeamID = owner->teamID;
			return KillPlayer(player);
		}
	}

	return false;
}

void CO_Shell::UsedAsStoredPowerup(CPlayer * player)
{
	owner = player;
	MoveToOwner();

	if(player->AcceptItem(this))
		state = 3;
	else
		Kick();
}

bool CO_Shell::KillPlayer(CPlayer * player)
{
	if(player->shield > 0 || player->invincible || player->shyguy)
		return false;

	CheckAndDie();

	//Find the player that shot this shell so we can attribute a kill
	PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_shell, false, false);

	CPlayer * killer = GetPlayerFromGlobalID(iPlayerID);
	if(killer && iPlayerID != player->globalID)
	{
		AddMovingKill(killer);
	}

	return true;
}

void CO_Shell::AddMovingKill(CPlayer * killer)
{
	if(state == 1 && game_values.awardstyle != award_style_none) //If the shell is moving, the keep track of how many people we kill in a row with it
	{
		if(++iKillCounter > 1)
			killer->AddKillsInRowInAirAward();
	}
}

void CO_Shell::collide(IO_MovingObject * object)
{
	if(object->GetDead())
		return;

	//Don't allow shells to die if they are warping
	if(owner && owner->iswarping())
		return;

	removeifprojectile(object, false, false);

	MovingObjectType type = object->getMovingObjectType();

	if(type == movingobject_shell)
	{
		CO_Shell * shell = (CO_Shell*)object;

		//Green shells should die on collision, other shells should not,
		//except if they also hit a non dead on collision shell

		if(frozen || shell->frozen)
		{
			Die();
			shell->Die();
		}
		if(shell->fSmoking && !fSmoking)
		{
			Die();
		}
		else if(!shell->fSmoking && fSmoking)
		{
			shell->Die();
		}
		else
		{
			if(fDieOnMovingPlayerCollision || state == 2 || (!shell->fDieOnMovingPlayerCollision && shell->state != 2))
				Die();

			if(shell->fDieOnMovingPlayerCollision || shell->state == 2 || (!fDieOnMovingPlayerCollision && state != 2))
				shell->Die();
		}
	}
	else if(type == movingobject_throwblock)
	{
		CO_ThrowBlock * block = (CO_ThrowBlock*)object;

		Die();
		block->Die();
	}
	else if(type == movingobject_throwbox)
	{
		CO_ThrowBox * box = (CO_ThrowBox*)object;

		Die();
		box->Die();
	}
	else if(type == movingobject_explosion || type == movingobject_sledgehammer || type == movingobject_superfireball)
	{
		Die();
	}
	else if(type == movingobject_fireball || type == movingobject_hammer || type == movingobject_boomerang)
	{
		if(fDieOnFire)
			Die();
	}
	else if(type == movingobject_iceblast)
	{
		frozenvelocity = velx;
		velx = 0.0f;
		animationspeed = 0;

		frozen = true;
		frozentimer = 300;
		
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX, iy - collisionOffsetY, 3, 8));
	}
}

void CO_Shell::update()
{
	if(iNoOwnerKillTime > 0)
		iNoOwnerKillTime--;

	if(frozen)
	{
		if(--frozentimer <= 0)
		{
			frozentimer = 0;
			frozen = false;
			
			velx = frozenvelocity;
			animationspeed = frozenanimationspeed;
		
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX, iy - collisionOffsetY, 3, 8));
		}
	}
	else
	{
		if(state == 1)
		{
			if(game_values.shellttl > 0 && ++iDeathTime >= game_values.shellttl)
			{
				eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix, iy, 3, 8));
				dead = true;
				ifsoundonplay(sfx_kicksound);

				if(owner)
				{
					owner->carriedItem = NULL;
					owner = NULL;
				}

				return;
			}
		}
		else
		{
			iDeathTime = 0;
		}
	}
	
	//Have the powerup grow out of the powerup block
	if(state == 0)
	{
		yf(fy - 2.0f);

		if(fy <= iDestY)
		{
			state = 2;
			vely = GRAVITATION;
			yf(iDestY);
		}

		return;
	}

	if(iIgnoreBounceTimer > 0)
		iIgnoreBounceTimer--;
	
	if(iBounceCounter > 0)
		iBounceCounter--;

	if(owner)
	{
		MoveToOwner();
		inair = true;
	}
	else
	{
		IO_MovingObject::update();
	}

	if(game_values.spotlights)
	{
		if(state == 1)
		{
			if(!sSpotlight)
			{
				sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
			}

			if(sSpotlight)
			{
				sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
			}
		}
		else
		{
			sSpotlight = NULL;
		}
	}
}

void CO_Shell::draw()
{
	if(state == 0)
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, iColorOffsetY, iw, (short)(ih - fy + iDestY));
	}
	else if(owner)
	{
		if(owner->iswarping())
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, iColorOffsetY + iFlippedOffset, iw, ih, (short)owner->state % 4, owner->GetWarpPlane());
		else
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, iColorOffsetY + iFlippedOffset, iw, ih);
	}
	else
	{
		if(state == 2)
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, iColorOffsetY + iFlippedOffset, iw, ih);
		else if(state == 1)
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iColorOffsetY + iFlippedOffset, iw, ih);
	}

	if(frozen)
	{
		spr_iceblock.draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, 0, 32, 32);
	}
	else if(fSmoking)
	{
		eyecandy[0].add(new EC_SingleAnimation(&spr_burnup, ix - collisionOffsetX + (iw >> 1) - 16, iy - collisionOffsetY + (ih >> 1) - 16, 5, 3));
	}
}

void CO_Shell::Drop()
{
	if(owner)
	{
		owner->carriedItem = NULL;
		xi(owner->ix + (owner->IsPlayerFacingRight() ? PW + 1: -31));
	}

	if(collision_detection_checksides())
	{
		//Move back to where it was before checking sides, then kill it
		xi(owner->ix + (owner->IsPlayerFacingRight() ? PW + 1: -31));
		yi(owner->iy + PH - 32 + collisionOffsetY);
		Die();
	}
	else
	{
		owner = NULL;
		state = 2;
	}
}

void CO_Shell::Kick()
{
	/*
	if(superkick)
	{
		vel = 10.0f;
		fSmoking = true;
		ifsoundonplay(sfx_cannon);
	}
	*/

	float fVel, fPlayerBonusVel = owner->velx / 2.0f;
	if(owner->IsPlayerFacingRight())
	{
		fVel = 5.0f;
		if(fPlayerBonusVel > 0.0f)
			fVel += fPlayerBonusVel;

		//if(fVel >= 7.5f)
		//	fSmoking = true;
	}
	else
	{
		fVel = -5.0f;
		if(fPlayerBonusVel < 0.0f)
			fVel += fPlayerBonusVel;

		//if(fVel <= -7.5f)
		//	fSmoking = true;
	}

	velx = fVel;
	vely = 0.0f;

	iPlayerID = owner->globalID;
	iTeamID = owner->teamID;

	owner = NULL;
	iNoOwnerKillTime = 30;

	state = 1;
	
	if(collision_detection_checksides())
		Die();
	else
		ifsoundonplay(sfx_kicksound);
}

void CO_Shell::CheckAndDie()
{
	if((fDieOnMovingPlayerCollision && state == 1) || ((fDieOnHoldingPlayerCollision || fFlipped)&& state == 3))
		Die();
	else if(!fDieOnHoldingPlayerCollision && state == 3 && (rand() % 5) == 0)
		Die();
}

void CO_Shell::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	eyecandy[2].add(new EC_FallingObject(&spr_shelldead, ix, iy, -velx / 4.0f, -VELJUMP / 2.0f, 1, 0, iShellType * 32, 0, 32, 32));
	dead = true;
	ifsoundonplay(sfx_kicksound);
	iKillCounter = 0;

	if(owner)
	{
		owner->carriedItem = NULL;
		owner = NULL;
	}
}

void CO_Shell::ShatterDie()
{
	dead = true;
	ifsoundonplay(sfx_breakblock);
	iKillCounter = 0;

	if(owner)
	{
		owner->carriedItem = NULL;
		owner = NULL;
	}

	short iBrokenIceX = ix - collisionOffsetX, iBrokenIceY = iy - collisionOffsetY;
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX, iBrokenIceY, -1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX + 16, iBrokenIceY, 1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX, iBrokenIceY + 16, -1.5f, -4.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, iBrokenIceX + 16, iBrokenIceY + 16, 1.5f, -4.0f, 4, 2, 0, 0, 16, 16));
}

void CO_Shell::SideBounce(bool fRightSide)
{
	if(state == 1)
	{
		if(iBounceCounter == 0)
		{
			eyecandy[2].add(new EC_SingleAnimation(&spr_shellbounce, ix + (velx > 0 ? 0 : collisionWidth) - 21, iy + (collisionHeight >> 1) - 20, 4, 4));
			ifsoundonplay(sfx_bump);

			iBounceCounter = 7; //Allow bounce stars to show on each bounce on a 2x wide pit
		}
	}
}

void CO_Shell::Flip()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	if(owner)
	{
		Die();
		return;
	}

	if(!fFlipped)
	{
		fFlipped = true;
		iFlippedOffset = 128;
	}

	Stop();
	vely = -VELJUMP / 2.0;
}

void CO_Shell::Stop()
{
	owner = NULL;
	velx = 0.0f;
	state = 2;
	fSmoking = false;
	ifsoundonplay(sfx_kicksound);
	iKillCounter = 0;
}

void CO_Shell::nospawn(short y, bool fBounce)
{
	state = 2;
	yi(y);
	
	if(fBounce)
		vely = -VELJUMP / 2.0;
}

//------------------------------------------------------------------------------
// class throwable block projectile
//------------------------------------------------------------------------------
//State 1: Moving
//State 2: Holding
CO_ThrowBlock::CO_ThrowBlock(gfxSprite * nspr, short x, short y, short type) :
	MO_CarriedObject(nspr, x, y, 4, 2, 30, 30, 1, 1)
{
	state = 2;
	ih = 32;
	movingObjectType = movingobject_throwblock;
	iPlayerID = -1;
	iTeamID = -1;
	
	fDieOnBounce = type != 2;
	fDieOnPlayerCollision = type == 0;

	iType = type;

	iDeathTime = 0;
	iBounceCounter = 0;
	iNoOwnerKillTime = 0;

	iOwnerRightOffset = 14;
	iOwnerLeftOffset = -22;
	iOwnerUpOffset = 32;

	frozen = false;
	frozentimer = 0;
	frozenvelocity = 0.0f;
	frozenanimationspeed = 2;

	sSpotlight = NULL;
}

bool CO_ThrowBlock::collide(CPlayer * player)
{
	if(frozen)
	{
		ShatterDie();
		return false;
	}

	if(player->fOldY + PH <= iy && player->iy + PH >= iy)
		return HitTop(player);
	else
		return HitOther(player);
}

bool CO_ThrowBlock::HitTop(CPlayer * player)
{
	if(player->invincible || player->shyguy)
	{
		Die();
	}
	else
	{
		if(state == 1) //moving
		{
			return KillPlayer(player);
		}
		else if(state == 2) //Holding
		{
			if(player != owner)
			{
				if(owner)
					owner->carriedItem = NULL;

				Kick();

				player->yi(iy - PH - 1);
				player->bouncejump();
				player->collision_detection_checktop();
				player->platform = NULL;
			}
		}
	}

	return false;
}

bool CO_ThrowBlock::HitOther(CPlayer * player)
{
	if(state == 1)  //Moving
	{
		short flipx = 0;

		if(player->ix + PW < 320 && ix > 320)
			flipx = 640;
		else if(ix + iw < 320 && player->ix > 320)
			flipx = -640;

		if(iNoOwnerKillTime == 0 || player->globalID != iPlayerID || (player->ix + flipx > ix + (iw >> 1) && velx > 0.0f) || (player->ix + flipx <= ix - (iw >> 1) && velx < 0.0f))
		{
			return KillPlayer(player);
		}
	}
	else if(state == 2)  //Holding
	{
		if(player != owner)
		{
			iPlayerID = owner->globalID;
			iTeamID = owner->teamID;
			return KillPlayer(player);
		}
	}

	return false;
}

bool CO_ThrowBlock::KillPlayer(CPlayer * player)
{
	if(player->invincible || player->shyguy)
	{
		Die();
		return false;
	}

	if(player->shield > 0)
		return false;

	CheckAndDie();

	//Find the player that shot this shell so we can attribute a kill
	PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_throwblock, false, false);
	return true;
}

void CO_ThrowBlock::collide(IO_MovingObject * object)
{
	if(object->GetDead())
		return;

	removeifprojectile(object, false, false);

	MovingObjectType type = object->getMovingObjectType();

	if(type == movingobject_throwblock)
	{
		CO_ThrowBlock * block = (CO_ThrowBlock*)object;

		Die();
		block->Die();
	}
	else if(type == movingobject_throwbox)
	{
		CO_ThrowBox * box = (CO_ThrowBox*)object;
		
		Die();
		box->Die();
	}
	else if(type == movingobject_explosion)
	{
		Die();
	}
	else if(type == movingobject_iceblast)
	{
		frozenvelocity = velx;
		velx = 0.0f;
		animationspeed = 0;

		frozen = true;
		frozentimer = 300;
		
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX, iy - collisionOffsetY, 3, 8));
	}
}

void CO_ThrowBlock::update()
{
	if(iNoOwnerKillTime > 0)
		iNoOwnerKillTime--;

	if(frozen)
	{
		if(--frozentimer <= 0)
		{
			frozentimer = 0;
			frozen = false;
			
			velx = frozenvelocity;
			animationspeed = frozenanimationspeed;
		
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX, iy - collisionOffsetY, 3, 8));
		}
	}
	else if(game_values.blueblockttl > 0 && ++iDeathTime >= game_values.blueblockttl)
	{
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix, iy, 3, 8));
		dead = true;

		if(owner)
		{
			owner->carriedItem = NULL;
			owner = NULL;
		}

		return;
	}
	
	if(owner)
	{
		MoveToOwner();
		inair = true;
	}
	else
	{
		if(iBounceCounter > 0)
			iBounceCounter--;

		fOldX = fx;
		fOldY = fy;

		collision_detection_map();
	}

	animate();

	if(game_values.spotlights && state == 1)
	{
		if(!sSpotlight)
		{
			sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
		}

		if(sSpotlight)
		{
			sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
		}
	}
}

void CO_ThrowBlock::draw()
{
	if(owner && owner->iswarping())
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iType << 5, iw, ih, (short)owner->state % 4, owner->GetWarpPlane());
	else
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, iType << 5, iw, ih);

	if(frozen)
	{
		spr_iceblock.draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, 0, 32, 32);
	}
	else if(fSmoking)
	{
		eyecandy[0].add(new EC_SingleAnimation(&spr_burnup, ix - collisionOffsetX + (iw >> 1) - 16, iy - collisionOffsetY + (ih >> 1) - 16, 5, 3));
	}
}

void CO_ThrowBlock::Drop()
{
	Kick();
}

void CO_ThrowBlock::Kick()
{
	/*
	if(superkick)
	{
		vel = 12.0f;
		fSmoking = true;
		ifsoundonplay(sfx_cannon);
	}
	*/

	iDeathTime = 0;

	float fVel, fPlayerBonusVel = owner->velx / 2.0f;
	if(owner->IsPlayerFacingRight())
	{
		fVel = 6.5f;
		if(fPlayerBonusVel > 0.0f)
			fVel += fPlayerBonusVel;

		//if(fVel >= 9.0f)
		//	fSmoking = true;
	}
	else
	{
		fVel = -6.5f;
		if(fPlayerBonusVel < 0.0f)
			fVel += fPlayerBonusVel;

		//if(fVel <= -9.0f)
		//	fSmoking = true;
	}

	velx = fVel;
	vely = 0.0f;

	iPlayerID = owner->globalID;
	iTeamID = owner->teamID;

	owner = NULL;
	iNoOwnerKillTime = 30;

	state = 1;

	if(collision_detection_checksides())
		Die();
	else
		ifsoundonplay(sfx_kicksound);
}

void CO_ThrowBlock::CheckAndDie()
{
	if(fDieOnPlayerCollision)
		Die();
}

void CO_ThrowBlock::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	if(dead)
		return;

	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix, iy, -1.5f, -7.0f, 6, 2, 0, iType << 4, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix + 16, iy, 1.5f, -7.0f, 6, 2, 0, iType << 4, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix, iy + 16, -1.5f, -4.0f, 6, 2, 0, iType << 4, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenblueblock, ix + 16, iy + 16, 1.5f, -4.0f, 6, 2, 0, iType << 4, 16, 16));

	DieHelper();
}

void CO_ThrowBlock::ShatterDie()
{
	if(dead)
		return;

	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix, iy, -1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix + 16, iy, 1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix, iy + 16, -1.5f, -4.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix + 16, iy + 16, 1.5f, -4.0f, 4, 2, 0, 0, 16, 16));

	game_values.unlocksecret2part2++;

	DieHelper();
}

void CO_ThrowBlock::DieHelper()
{
	dead = true;
	ifsoundonplay(sfx_breakblock);

	if(owner)
	{
		owner->carriedItem = NULL;
		owner = NULL;
	}
}

void CO_ThrowBlock::SideBounce(bool fRightSide)
{
	if(fDieOnBounce)
	{
		Die();
	}
	else if(state == 1)
	{
		if(iBounceCounter == 0)
		{
			eyecandy[2].add(new EC_SingleAnimation(&spr_shellbounce, ix + (velx > 0 ? 0 : collisionWidth) - 21, iy + (collisionHeight >> 1) - 20, 4, 4));
			ifsoundonplay(sfx_bump);

			iBounceCounter = 7; //Allow bounce stars to show on each bounce on a 2x wide pit
		}
	}
}

//------------------------------------------------------------------------------
// class throwable box - can be used as a shield, thrown at a player, or holds items
//------------------------------------------------------------------------------
CO_ThrowBox::CO_ThrowBox(gfxSprite * nspr, short x, short y, short item) :
	MO_CarriedObject(nspr, x, y, 4, 8, 30, 30, 1, 1)
{
	state = 1;
	ih = 32;
	iw = 32;

	movingObjectType = movingobject_throwbox;

	iPlayerID = -1;
	iTeamID = -1;
	
	iItem = item;

	iOwnerRightOffset = 14;
	iOwnerLeftOffset = -22;
	iOwnerUpOffset = 32;

	frozen = false;
	frozentimer = 0;
	frozenanimationspeed = 8;

	sSpotlight = NULL;
}

bool CO_ThrowBox::collide(CPlayer * player)
{
	//Kill the player if it is moving

	if(frozen)
	{
		ShatterDie();
		return false;
	}

	if(HasKillVelocity())
	{
		short flipx = 0;

		if(player->ix + PW < 320 && ix > 320)
			flipx = 640;
		else if(ix + iw < 320 && player->ix > 320)
			flipx = -640;

		if(player->globalID != iPlayerID)
		{
			return KillPlayer(player);
		}
	}
	/*
	//Kill player when another player is holding the box
	else
	{
		if(owner && player != owner && (game_values.teamcollision == 2 || player->teamID != owner->teamID))
		{
			iPlayerID = owner->globalID;
			iTeamID = owner->teamID;
			return KillPlayer(player);
		}
	}*/

	//Otherwise allow them to pick this box up
	if(owner == NULL && player->isready())
	{
		if(player->AcceptItem(this))
		{
			owner = player;
		}
	}

	return false;
}


bool CO_ThrowBox::KillPlayer(CPlayer * player)
{
	if(player->invincible || player->shyguy)
	{
		Die();
		return false;
	}

	if(player->shield > 0)
		return false;

	Die();

	//Find the player that shot this shell so we can attribute a kill
	PlayerKilledPlayer(iPlayerID, player, death_style_jump, kill_style_throwblock, false, false);
	return true;
}

void CO_ThrowBox::collide(IO_MovingObject * object)
{
	if(object->GetDead())
		return;

	removeifprojectile(object, false, false);

	MovingObjectType type = object->getMovingObjectType();

	if(type == movingobject_throwbox)
	{
		CO_ThrowBox * box = (CO_ThrowBox*)object;
		if(frozen || box->frozen || HasKillVelocity() || box->HasKillVelocity())
		{
			Die();
			box->Die();
		}
	}
	else if(type == movingobject_explosion || type == movingobject_fireball || type == movingobject_hammer || type == movingobject_boomerang || type == movingobject_superfireball || type == movingobject_sledgehammer)
	{
		Die();
	}
	else if(type == movingobject_iceblast)
	{
		velx = 0.0f;
		animationspeed = 0;

		frozen = true;
		frozentimer = 300;
		
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX, iy - collisionOffsetY, 3, 8));
	}
}

void CO_ThrowBox::update()
{	
	if(frozen)
	{
		if(--frozentimer <= 0)
		{
			frozentimer = 0;
			frozen = false;
			
			animationspeed = frozenanimationspeed;
		
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - collisionOffsetX, iy - collisionOffsetY, 3, 8));
		}
	}
	
	fOldX = fx;
	fOldY = fy;

	if(owner)
	{
		MoveToOwner();
		inair = true;
	}
	else
	{
		applyfriction();
		collision_detection_map();
	}

	animate();

	if(game_values.spotlights && HasKillVelocity())
	{
		if(!sSpotlight)
		{
			sSpotlight = spotlightManager.AddSpotlight(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1), 3);
		}

		if(sSpotlight)
		{
			sSpotlight->UpdatePosition(ix - collisionOffsetX + (iw >> 1), iy - collisionOffsetY + (ih >> 1));
		}
	}
}

void CO_ThrowBox::draw()
{
	if(owner && owner->iswarping())
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, 0, iw, ih, (short)owner->state % 4, owner->GetWarpPlane());
	else
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, 0, iw, ih);

	if(frozen)
	{
		spr_iceblock.draw(ix - collisionOffsetX, iy - collisionOffsetY, 0, 0, 32, 32);
	}
}

void CO_ThrowBox::Drop()
{
	if(owner)
	{
		iPlayerID = owner->globalID;
		iTeamID = owner->teamID;
	}

	if(collision_detection_checksides())
		Die();
	else
		MO_CarriedObject::Drop();
}

void CO_ThrowBox::Kick()
{
	if(owner)
	{
		iPlayerID = owner->globalID;
		iTeamID = owner->teamID;
	}

	if(collision_detection_checksides())
		Die();
	else
		MO_CarriedObject::Kick();
}

void CO_ThrowBox::Die()
{
	if(frozen)
	{
		ShatterDie();
		return;
	}

	if(dead)
		return;

	eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix, iy, -2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix + 16, iy, 2.2f, -10.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix, iy + 16, -2.2f, -5.5f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokenyellowblock, ix + 16, iy + 16, 2.2f, -5.5f, 4, 2, 0, 0, 16, 16));

	DieHelper();
}

void CO_ThrowBox::ShatterDie()
{
	if(dead)
		return;

	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix, iy, -1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix + 16, iy, 1.5f, -7.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix, iy + 16, -1.5f, -4.0f, 4, 2, 0, 0, 16, 16));
	eyecandy[2].add(new EC_FallingObject(&spr_brokeniceblock, ix + 16, iy + 16, 1.5f, -4.0f, 4, 2, 0, 0, 16, 16));

	game_values.unlocksecret2part2++;

	DieHelper();
}

void CO_ThrowBox::DieHelper()
{
	dead = true;
	ifsoundonplay(sfx_breakblock);

	if(owner)
	{
		owner->carriedItem = NULL;
		owner = NULL;
	}

	//Check to see if we should spawn an item here
	if(iItem != NO_POWERUP)
	{
		createpowerup(iItem, ix, iy, velx < 0.0f, false);
	}
}

void CO_ThrowBox::SideBounce(bool fRightSide)
{
	if(dead)
		return;

	if(HasKillVelocity())
	{
		if(frozen)
			ShatterDie();
		else
			Die();
	}
}

float CO_ThrowBox::BottomBounce()
{
	if(dead)
		return bounce;

	if(HasKillVelocity())
	{
		if(frozen)
			ShatterDie();
		else
			Die();
	}
	
	return bounce;
}

bool CO_ThrowBox::HasKillVelocity()
{
	return velx < -0.01f || velx > 0.01f || vely < -0.01f || vely > 2.0f;
}

//------------------------------------------------------------------------------
// class spring
//------------------------------------------------------------------------------
CO_Spring::CO_Spring(gfxSprite *nspr, short ix, short iy, bool fsuper) :
	MO_CarriedObject(nspr, ix, iy, 4, 4, 30, 31, 1, 0)
{
	fSuper = fsuper;
	iOffsetY = fSuper ? 32 : 0;

	state = 1;
	movingObjectType = movingobject_carried;

	iOwnerRightOffset = 14;
	iOwnerLeftOffset = -22;
	iOwnerUpOffset = 32;
}

bool CO_Spring::collide(CPlayer * player)
{
	if(owner == NULL)
	{
		if(player->fOldY + PH <= fOldY && player->iy + PH >= iy)
			hittop(player);
		else if(state == 1)
			hitother(player);
	}
	
	return false;
}

void CO_Spring::hittop(CPlayer * player)
{
	state = 2;
	drawframe += iw;

	player->yi(iy - PH - 1);
	player->collision_detection_checktop();
	player->platform = NULL;
	player->inair = false;
	player->fallthrough = false;
	player->killsinrowinair = 0;
	player->extrajumps = 0;

	player->superjumptimer = 4;
	player->superjumptype = fSuper ? 2 : 1;
	player->vely = -VELNOTEBLOCKREPEL;
	
	ifsoundonplay(sfx_bump);
}

void CO_Spring::hitother(CPlayer * player)
{
	if(owner == NULL && player->isready())
	{
		if(player->AcceptItem(this))
		{
			owner = player;
		}
	}
}

void CO_Spring::update()
{
	if(owner)
	{
		MoveToOwner();
	}
	else
	{
		applyfriction();

		//Collision detect map
		fOldX = fx;
		fOldY = fy;

		collision_detection_map();
	}

	if(state == 2)
	{
		if(++animationtimer == animationspeed)
		{
			animationtimer = 0;
			
			drawframe += iw;
			if(drawframe >= animationWidth)
			{
				drawframe = 0;
				state = 1;
			}
		}
	}
}

void CO_Spring::draw()
{
	if(owner)
	{
		if(owner->iswarping())
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, animationOffsetX, iOffsetY, 32, 32, (short)owner->state % 4, owner->GetWarpPlane());
		else
			spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, animationOffsetX, iOffsetY, 32, 32);
	}
	else
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, animationOffsetX + drawframe, iOffsetY, 32, 32);
	}
}

void CO_Spring::place()
{
	short iAttempts = 10;
	while(!g_map.findspawnpoint(5, &ix, &iy, collisionWidth, collisionHeight, false) && iAttempts-- > 0);
	fx = (float)ix;
	fy = (float)iy;

	Drop();
}


//------------------------------------------------------------------------------
// class spike
//------------------------------------------------------------------------------
CO_Spike::CO_Spike(gfxSprite *nspr, short ix, short iy) :
	CO_Spring(nspr, ix, iy, false)
{
	iw = 32;
	ih = 32;

	movingObjectType = movingobject_carried;
}

void CO_Spike::hittop(CPlayer * player)
{
	if(player->isready() && player->shield == 0 && !player->invincible && player->iKuriboShoe == 0 && !player->shyguy)
		player->KillPlayerMapHazard(false, kill_style_environment, false);
}


//------------------------------------------------------------------------------
// class kuribo's shoe
//------------------------------------------------------------------------------
CO_KuriboShoe::CO_KuriboShoe(gfxSprite *nspr, short ix, short iy, bool sticky) :
	CO_Spring(nspr, ix, iy + 15, false)
{
	iw = 32;
	ih = 32;

	collisionOffsetY = 15;
	collisionHeight = 16;

	animationOffsetX = sticky ? 64 : 0;

	movingObjectType = movingobject_carried;

	fSticky = sticky;
}

void CO_KuriboShoe::hittop(CPlayer * player)
{
	if(player->iKuriboShoe == 0 && player->statue_timer == 0)
	{
		dead = true;
		player->SetKuriboShoe(fSticky ? 2 : 1);
		ifsoundonplay(sfx_transform);
		eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, player->ix + HALFPW - 16, player->iy + HALFPH - 16, 3, 8));
	}
}


//------------------------------------------------------------------------------
// attack zone(invisible area that kills objects and players)
//------------------------------------------------------------------------------
MO_AttackZone::MO_AttackZone(short playerId, short teamId, short x, short y, short w, short h, short time, killstyle style, bool dieoncollision) :
	IO_MovingObject(NULL, x, y, 1, 0, w, h, 0, 0)
{
	iPlayerID = playerId;
	iTeamID = teamId;
	iStyle = style;

	objectType = object_moving;
	movingObjectType = movingobject_attackzone;

	iTimer = time;
	fDieOnCollision = dieoncollision;

	state = 1;

	fObjectCollidesWithMap = false;
}

bool MO_AttackZone::collide(CPlayer * player)
{
	if(player->shield > 0 || player->invincible || player->shyguy || dead)
		return false;

	if(game_values.teamcollision != 2 && player->teamID == iTeamID)
		return false;

	CPlayer * killer = GetPlayerFromGlobalID(iPlayerID);

	if(killer && killer->globalID == player->globalID)
		return false;

	PlayerKilledPlayer(iPlayerID, player, death_style_jump, iStyle, false, false);

	Die();

	return true;
}

/*void MO_AttackZone::draw()
{
	if(!dead)
	{
		SDL_Rect r = {ix, iy, collisionWidth, collisionHeight};
		SDL_FillRect(blitdest, &r, 0xf000);
	}
}*/

void MO_AttackZone::update()
{
	if(--iTimer <= 0)
		dead = true;
}

void MO_AttackZone::collide(IO_MovingObject * object)
{
	if(dead)
		return;

	MovingObjectType type = object->getMovingObjectType();

	if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox)
	{
		if(type == movingobject_shell)
		{
			CO_Shell * shell = (CO_Shell*)object;
			if(shell->frozen)
				shell->ShatterDie();
			else
				shell->Die();
		}
		else if(type == movingobject_throwblock)
		{
			CO_ThrowBlock * block = (CO_ThrowBlock*) object;

			if(block->frozen)
				block->ShatterDie();
			else if(!block->owner || block->owner->globalID != iPlayerID)
				block->Die();
		}
		else if(type == movingobject_throwbox)
		{
			CO_ThrowBox * box = (CO_ThrowBox*) object;

			if(box->frozen)
				box->ShatterDie();
			else if(!box->owner || box->owner->globalID != iPlayerID)
				box->Die();
		}

		ifsoundonplay(sfx_kicksound);
	}	
}

void MO_AttackZone::Die()
{
	if(fDieOnCollision)
		dead = true;
}


//------------------------------------------------------------------------------
// class spin death (spinning cape or tail)
//------------------------------------------------------------------------------
MO_SpinAttack::MO_SpinAttack(short playerId, short teamId, killstyle style, bool direction, short offsety) :
	MO_AttackZone(playerId, teamId, 0, 0, 24, 12, 16, style, true)
{
	fDirection = direction;
	iOffsetY = offsety;

	state = 0;
	objectType = object_moving;
}

bool MO_SpinAttack::collide(CPlayer * player)
{
	if(iTimer > 11)
		return false;

	return MO_AttackZone::collide(player);
}

/*
void MO_SpinAttack::draw()
{
	if(iTimer <= 11 && !dead)
	{
		SDL_Rect r = {ix, iy, collisionWidth, collisionHeight};
		SDL_FillRect(blitdest, &r, 0xff00);
	}
}
*/

void MO_SpinAttack::update()
{
	MO_AttackZone::update();

	if(iTimer <= 11)
		state = 1;

	CPlayer * owner = GetPlayerFromGlobalID(iPlayerID);

	if(owner)
	{
		//Move to the owner
		xi(owner->ix - PWOFFSET + (fDirection ? 24 : -16));
		yi(owner->iy + PH - iOffsetY);

		if(iTimer < 5 || iy + collisionHeight < 0)
			return;

		//Check block collisions
		short iTop = iy / TILESIZE;
		short iBottom = (iy + collisionHeight) / TILESIZE;

		short iLeft;
		if(ix < 0)			
			iLeft = (ix + 640) / TILESIZE;
		else
			iLeft = ix / TILESIZE;

		short iRight = (ix + collisionWidth) / TILESIZE;

		if(iLeft < 0)
			iLeft += 20;

		if(iLeft >= 20)
			iLeft -= 20;

		if(iRight < 0)
			iRight += 20;

		if(iRight >= 20)
			iRight -= 20;

		IO_Block * topleftblock = NULL;
		IO_Block * toprightblock = NULL;
		IO_Block * bottomleftblock = NULL;
		IO_Block * bottomrightblock = NULL;

		if(iTop >= 0 && iTop < 15)
		{
			topleftblock = g_map.block(iLeft, iTop);
			toprightblock = g_map.block(iRight, iTop);
		}

		if(iBottom >= 0 && iBottom < 15)
		{
			bottomleftblock = g_map.block(iLeft, iBottom);
			bottomrightblock = g_map.block(iRight, iBottom);
		}

		bool fHitBlock = false;
		if(topleftblock && !topleftblock->isTransparent() && !topleftblock->isHidden())
			fHitBlock = topleftblock->collide(this, 3);
		
		if(!fHitBlock && toprightblock && !toprightblock->isTransparent() && !toprightblock->isHidden())
			fHitBlock = toprightblock->collide(this, 1);

		if(!fHitBlock && bottomleftblock && !bottomleftblock->isTransparent() && !bottomleftblock->isHidden())
			fHitBlock = bottomleftblock->collide(this, 3);
		
		if(!fHitBlock && bottomrightblock && !bottomrightblock->isTransparent() && !bottomrightblock->isHidden())
			fHitBlock = bottomrightblock->collide(this, 1);

		if(fHitBlock)
			dead = true;
	}
	else
	{
		dead = true;
	}
}

void MO_SpinAttack::collide(IO_MovingObject * object)
{
	if(dead || iTimer > 11)
		return;

	MovingObjectType type = object->getMovingObjectType();

	if(type == movingobject_shell || type == movingobject_throwblock || type == movingobject_throwbox)
	{
		if(type == movingobject_shell)
		{
			CO_Shell * shell = (CO_Shell*)object;
			
			if(shell->frozen)
			{
				shell->ShatterDie();
			}
			else
			{
				shell->Flip();
				ifsoundonplay(sfx_kicksound);
			}
			
			Die();
		}
		else if(type == movingobject_throwblock)
		{
			CO_ThrowBlock * block = (CO_ThrowBlock*) object;

			if(block->frozen)
			{
				block->ShatterDie();
			}
			else if(!block->owner || block->owner->globalID != iPlayerID)
			{
				block->Die();
				ifsoundonplay(sfx_kicksound);
				Die();
			}
		}
		else if(type == movingobject_throwbox)
		{
			CO_ThrowBox * box = (CO_ThrowBox*) object;

			if(box->frozen)
			{
				box->ShatterDie();
			}
			else if(!box->owner || box->owner->globalID != iPlayerID)
			{
				box->Die();
				ifsoundonplay(sfx_kicksound);
				Die();
			}
		}
	}	
}


//------------------------------------------------------------------------------
// class pipe coin (for coin pipe minigame)
//------------------------------------------------------------------------------
OMO_PipeCoin::OMO_PipeCoin(gfxSprite *nspr, float dvelx, float dvely, short ix, short iy, short teamid, short colorid, short uncollectabletime) :
	IO_OverMapObject(nspr, ix, iy, 4, 8, 30, 30, 1, 1, 0, colorid << 5, 32, 32)
{
	iTeamID = teamid;
	iColorID = colorid;
	state = 1;
	objectType = object_pipe_coin;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;

	velx = dvelx;
	
	if(pipegamemode->IsSlowdown())
		vely = dvely / 2.0f;
	else
		vely = dvely;

	iUncollectableTime = uncollectabletime;
}

bool OMO_PipeCoin::collide(CPlayer * player)
{
	if(iUncollectableTime > 0)
		return false;

	if(!game_values.gamemode->gameover)
	{
		if(iTeamID != -1)
		{
			if(player->teamID == iTeamID)
			{
				player->score->AdjustScore(1);
				ifsoundonplay(sfx_coin);
			}
		}
		else
		{
			if(iColorID == 2)
			{
				player->score->AdjustScore(1);
				ifsoundonplay(sfx_coin);
			}
			else if(iColorID == 0)
			{
				player->score->AdjustScore(-1);
				ifsoundonplay(sfx_stun);
			}
			else if(iColorID == 1)
			{
				player->score->AdjustScore(5);
				ifsoundonplay(sfx_extraguysound);
			}
		}

		game_values.gamemode->CheckWinner(player);
	}

	eyecandy[2].add(new EC_SingleAnimation(&spr_coinsparkle, ix, iy, 7, 4));

	dead = true;
	return false;
}

void OMO_PipeCoin::update()
{
	xf(fx + velx);
	yf(fy + vely);

	if(iTeamID == -1)
		animate();

	if(iy >= 480)
		dead = true;

	if(pipegamemode->IsSlowdown())
		vely += GRAVITATION / 1.5f;
	else
		vely += GRAVITATION;
	
	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}

	if(iUncollectableTime > 0)
		--iUncollectableTime;
}

void OMO_PipeCoin::draw()
{
	if(iUncollectableTime > 0)
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY, iw, ih, 2, 256);

		//Draw sparkles
		if(iTeamID == -1)
			spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32, 2, 256);

	}
	else
	{
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY, iw, ih);

		//Draw sparkles
		if(iTeamID == -1)
			spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
	}
}

//------------------------------------------------------------------------------
// class pipe powerup (for coin pipe minigame)
//------------------------------------------------------------------------------
OMO_PipeBonus::OMO_PipeBonus(gfxSprite *nspr, float dvelx, float dvely, short ix, short iy, short type, short duration, short uncollectabletime) :
	IO_OverMapObject(nspr, ix, iy, 4, 8, 30, 30, 1, 1, 0, type << 5, 32, 32)
{
	iType = type;
	iDuration = duration;
	state = 1;
	objectType = object_pipe_bonus;

	velx = dvelx;

	if(pipegamemode->IsSlowdown())
		vely = dvely / 1.5f;
	else
		vely = dvely;

	iUncollectableTime = uncollectabletime;
}

bool OMO_PipeBonus::collide(CPlayer * player)
{
	if(iUncollectableTime > 0)
		return false;

	//fireball
	if(iType == 5)
	{
		if(player->shield == 0)
		{
			dead = true;
			eyecandy[2].add(new EC_SingleAnimation(&spr_fireballexplosion, ix - 1, iy - 1, 3, 8));

			if(!player->invincible)
			{
				return player->KillPlayerMapHazard(false, kill_style_environment, false) != player_kill_nonkill;
			}
		}

		return false;
	}
	else
	{
		if(!game_values.gamemode->gameover)
			pipegamemode->SetBonus(iType + 1, iDuration, player->getTeamID());
	}

	dead = true;
	return false;
}

void OMO_PipeBonus::update()
{
	IO_OverMapObject::update();

	if(iy >= 480)
		dead = true;

	if(pipegamemode->IsSlowdown())
		vely += GRAVITATION / 2.0f;
	else
		vely += GRAVITATION;
	
	if(iUncollectableTime > 0)
		--iUncollectableTime;
}

void OMO_PipeBonus::draw()
{
	if(iUncollectableTime > 0)
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY, iw, ih, 2, 256);
	else
		spr->draw(ix - collisionOffsetX, iy - collisionOffsetY, drawframe, animationOffsetY, iw, ih);
}

//------------------------------------------------------------------------------
// class Phanto (for phanto mode)
//------------------------------------------------------------------------------
OMO_Phanto::OMO_Phanto(gfxSprite *nspr, short x, short y, float dVelX, float dVelY, short type) :
	IO_OverMapObject(nspr, x, y, 1, 0, 30, 32, 1, 0, type << 5, 0, 32, 32)
{
	objectType = object_phanto;
	velx = dVelX;
	vely = dVelY;

	iType = type;

	dMaxSpeedY = (float)game_values.gamemodesettings.chase.phantospeed / 2.0f;
	dMaxSpeedX = dMaxSpeedY + 1.0f;
	dSpeedRatio = (float)game_values.gamemodesettings.chase.phantospeed / 6.0f;

	iSpeedTimer = 0;
	dReactionSpeed = 0.2f;
}

void OMO_Phanto::update()
{
	xf(fx + velx);
	yf(fy + vely);

	if(fx < 0.0f)
		xf(fx + 640.0f);
	else if(fx + iw >= 640.0f)
		xf(fx - 640.0f);

	if(++iSpeedTimer > 62)
	{
		iSpeedTimer = 0;
		dReactionSpeed = (0.05f + (float)(rand() % 20) / 100.0f) * dSpeedRatio;
	}

	//Chase player or move off screen if there is no player holding the key
	if(game_values.gamemode->gamemode == game_mode_chase)
	{
		CGM_Chase * chasemode = (CGM_Chase*)game_values.gamemode;

		CPlayer * player = chasemode->GetKeyHolder();

		if(!game_values.gamemode->gameover && player) //Chase the player
		{
			bool fWrap = false;
			if(abs(player->ix - ix) > 320)
				fWrap = true;

			//Chase wrapped around edge of screen
			if((player->ix < ix && !fWrap) || (player->ix > ix && fWrap)) 
			{
				velx -= dReactionSpeed;
				
				if(velx < -dMaxSpeedX)
					velx = -dMaxSpeedX;
			}
			else 
			{
				velx += dReactionSpeed;

				if(velx > dMaxSpeedX)
					velx = dMaxSpeedX;
			}

			if(player->iy < iy)
			{
				vely -= dReactionSpeed;
				
				if(vely < -dMaxSpeedY)
					vely = -dMaxSpeedY;
			}
			else 
			{
				vely += dReactionSpeed;

				if(vely > dMaxSpeedY)
					vely = dMaxSpeedY;
			}
		}
		else //Wander off screen
		{
			if(iy > 240)
			{
				vely += dReactionSpeed;

				if(vely > dMaxSpeedY)
					vely = dMaxSpeedY;
			}
			else
			{
				vely -= dReactionSpeed;
				
				if(vely < -dMaxSpeedY)
					vely = -dMaxSpeedY;
			}

			if(iy >= 480 || iy + ih < -CRUNCHMAX)
			{
				vely = 0.0f;
				velx = 0.0f;

				//Randomly position phanto off screen
				xi(rand() % 640);
				yi(rand() % 2 == 0 ? -ih - CRUNCHMAX: 480);
			}
		}
	}
}

bool OMO_Phanto::collide(CPlayer * player)
{
	if(iy <= -ih || iy >= 480)
		return false;

	//If the player is holding the key, kill him
	if(!player->invincible && player->shield == 0)
	{
		if(iType == 2)
		{
			player->KillPlayerMapHazard(false, kill_style_phanto, false);
			return true;
		}
		else
		{
			if(game_values.gamemode->gamemode == game_mode_chase)
			{
				CGM_Chase * chasemode = (CGM_Chase*)game_values.gamemode;
				CPlayer * keyholder = chasemode->GetKeyHolder();

				if(keyholder == player)
				{
					player->KillPlayerMapHazard(false, kill_style_phanto, false);

					if(!game_values.gamemode->gameover && iType == 1)
					{
						player->score->AdjustScore(-10);
						ifsoundonplay(sfx_stun);
					}

					return true;
				}
			}
		}
	}

	return false;
}


//------------------------------------------------------------------------------
// class phanto key (for chase mode)
//------------------------------------------------------------------------------
CO_PhantoKey::CO_PhantoKey(gfxSprite *nspr) :
	MO_CarriedObject(nspr, 0, 0, 1, 0, 30, 30, 1, 1, 0, 0, 32, 32)
{
	state = 1;
	movingObjectType = movingobject_phantokey;

	iOwnerRightOffset = 12;
	iOwnerLeftOffset = -20;
	iOwnerUpOffset = 32;

	sparkleanimationtimer = 0;
	sparkledrawframe = 0;

	fCarriedByKuriboShoe = true;

	placeKey();
}

bool CO_PhantoKey::collide(CPlayer * player)
{
	if(owner == NULL && player->isready())
	{
		if(player->AcceptItem(this))
		{
			owner = player;
		}
	}

	return false;
}

void CO_PhantoKey::update()
{
	if(owner)
	{
		MoveToOwner();
		relocatetimer = 0;
	}
	else if(++relocatetimer > 1000)
	{
		placeKey();
	}
	else
	{
		applyfriction();

		//Collision detect map
		fOldX = fx;
		fOldY = fy;

		collision_detection_map();
	}

	if(++sparkleanimationtimer >= 4)
	{
		sparkleanimationtimer = 0;
		sparkledrawframe += 32;
		if(sparkledrawframe >= 480)
			sparkledrawframe = 0;
	}
}

void CO_PhantoKey::draw()
{
	MO_CarriedObject::draw();

	spr_shinesparkle.draw(ix - collisionOffsetX, iy - collisionOffsetY, sparkledrawframe, 0, 32, 32);
}

void CO_PhantoKey::placeKey()
{
	relocatetimer = 0;
	
	short x = 0, y = 0;
	short iAttempts = 10;
	while(!g_map.findspawnpoint(5, &x, &y, collisionWidth, collisionHeight, false) && iAttempts-- > 0);

	xi(x);
	yi(y);

	vely = GRAVITATION;
	velx = 0.0f;

	Drop();
}


void MysteryMushroomTempPlayer::SetPlayer(CPlayer * player, short iPowerup)
{
	fx = player->fx;
	fy = player->fy;

	fOldX = player->fOldX;
	fOldY = player->fOldY;

	velx = player->velx;
	vely = player->vely;

	//bobomb = player->bobomb;
	//powerup = player->powerup;
	
	burnupstarttimer = player->burnupstarttimer;
	burnuptimer = player->burnuptimer;

	inair = player->inair;
	onice = player->onice;
	//invincible = player->invincible;
	//invincibletimer = player->invincibletimer;

	platform = player->platform;
	//iCapeFrameX = player->iCapeFrameX;
	//iCapeFrameY = player->iCapeFrameY;
	//iCapeTimer = player->iCapeTimer;
	//iCapeYOffset = player->iCapeYOffset;

	gamepowerup = iPowerup;

	iOldPowerupX = player->score->x + scorepowerupoffsets[game_values.teamcounts[player->teamID] - 1][player->subTeamID];
	iOldPowerupY = player->score->y + 25;
}
		
void MysteryMushroomTempPlayer::GetPlayer(CPlayer * player, short * iPowerup)
{
	player->fNewSwapX = fx;
	player->fNewSwapY = fy;

	player->iOldPowerupX = iOldPowerupX;
	player->iOldPowerupY = iOldPowerupY;

	player->fOldX = fOldX;
	player->fOldY = fOldY;

	player->velx = velx;
	player->vely = vely;

	//player->bobomb = bobomb;
	//player->powerup = powerup;
	
	player->burnupstarttimer = burnupstarttimer;
	player->burnuptimer = burnuptimer;

	player->inair = inair;
	player->onice = onice;
	//player->invincible = invincible;
	//player->invincibletimer = invincibletimer;

	player->platform = platform;
	//player->iCapeFrameX = iCapeFrameX;
	//player->iCapeFrameY	= iCapeFrameY;
	//player->iCapeTimer = iCapeTimer;
	//player->iCapeYOffset = iCapeYOffset;

	*iPowerup = gamepowerup;

	if(player->carriedItem)
		player->carriedItem->MoveToOwner();
}

